@Test
  public void testBucketingWithEmptyIndexToBegin()
      throws InterruptedException, StoreException, IOException, TimeoutException {
    assumeTrue(bucketingEnabled);
    state.destroy();
    assertTrue(tempDir.getAbsolutePath() + " could not be deleted", StoreTestUtils.cleanDirectory(tempDir, true));
    tempDir = StoreTestUtils.createTempDirectory("blobStoreStatsDir-" + TestUtils.getRandomString(10));
    state = new CuratedLogIndexState(true, tempDir, false, false, true, true, false, false);
    MockThrottler mockThrottler = new MockThrottler(new CountDownLatch(0), new CountDownLatch(0));
    throttlers.put(BlobStoreStats.IO_SCHEDULER_JOB_TYPE, mockThrottler);
    int bucketCount = 50;
    BlobStoreStats blobStoreStats = setupBlobStoreStats(bucketCount, 0);
    // Make sure the index scanner is finished and we can enqueue
    if (!TestUtils.checkAndSleep(() -> blobStoreStats.isRecentEntryQueueEnabled(), 10000)) {
      throw new TimeoutException("Time out to wait for IndexScanner to finish");
    }
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, state.time.milliseconds());
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(state.time.milliseconds(), 0));
    long expiresAtInMs = state.time.milliseconds() + ((long) bucketCount - 2) * BUCKET_SPAN_IN_MS;
    // 3 new puts with expiry
    List<IndexEntry> newPutEntries = state.addPutEntries(3, PUT_RECORD_SIZE, expiresAtInMs);
    // 3 new put with no expiry
    newPutEntries.addAll(state.addPutEntries(3, PUT_RECORD_SIZE, Utils.Infinite_Time));
    for (IndexEntry entry : newPutEntries) {
      blobStoreStats.handleNewPutEntry(entry.getKey(), entry.getValue());
    }
    // delete one of the put with expiry
    MockId putWithExpiry = getIdToDelete(newPutEntries.get(0).getKey());
    newDelete(blobStoreStats, putWithExpiry);
    // delete one of the put without expiry
    MockId putWithoutExpiry = getIdToDelete(newPutEntries.get(newPutEntries.size() - 1).getKey());
    newDelete(blobStoreStats, putWithoutExpiry);
    // a probe put with a latch to inform us about the state of the queue
    CountDownLatch queueProcessedLatch = new CountDownLatch(1);
    blobStoreStats.handleNewPutEntry(null, new MockIndexValue(queueProcessedLatch, state.index.getCurrentEndOffset()));
    assertTrue("QueueProcessor took too long to process the new entries",
        queueProcessedLatch.await(3, TimeUnit.SECONDS));
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, state.time.milliseconds());
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(state.time.milliseconds(), 0));
    advanceTimeToNextSecond();
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, state.time.milliseconds());
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(state.time.milliseconds(), 0));
    long timeToLiveInMs = expiresAtInMs - state.time.milliseconds() < 0 ? 0 : expiresAtInMs - state.time.milliseconds();
    state.advanceTime(timeToLiveInMs + Time.MsPerSec);
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, state.time.milliseconds());
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(state.time.milliseconds(), 0));
    blobStoreStats.close();
  }
Tests to verify requests inside and outside of the forecast coverage can still be served properly while scanning.
@throws Exception
ambry-store/src/test/java/com/github/ambry/store/BlobStoreStatsTest.java