@Test
  public void testBucketingCoverageTransition() throws Exception {
    assumeTrue(bucketingEnabled);
    CountDownLatch scanStartedLatch = new CountDownLatch(1);
    // do not hold the initial scan
    MockThrottler mockThrottler = new MockThrottler(scanStartedLatch, new CountDownLatch(0), 50, 100);
    throttlers.put(BlobStoreStats.IO_SCHEDULER_JOB_TYPE, mockThrottler);
    // add a put that is going to expire in 20 seconds
    long expiresAtInMs = state.time.milliseconds() + TimeUnit.SECONDS.toMillis(20);
    state.addPutEntries(1, PUT_RECORD_SIZE, expiresAtInMs);
    long initialScanTimeInMs = state.time.milliseconds();
    BlobStoreStats blobStoreStats = setupBlobStoreStats(1, 0);
    int expectedThrottleCount = state.referenceIndex.size();
    // proceed only when the scan is started
    assertTrue("IndexScanner took too long to start", scanStartedLatch.await(5, TimeUnit.SECONDS));
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(initialScanTimeInMs, 0L));
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, initialScanTimeInMs);
    state.advanceTime(expiresAtInMs - state.time.milliseconds());
    // hold the next scan
    CountDownLatch scanHoldLatch = new CountDownLatch(1);
    scanStartedLatch = new CountDownLatch(1);
    mockThrottler.holdLatch = scanHoldLatch;
    mockThrottler.startedLatch = scanStartedLatch;
    mockThrottler.isThrottlerStarted = false;
    assertTrue("IndexScanner took too long to start", scanStartedLatch.await(5, TimeUnit.SECONDS));
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(initialScanTimeInMs, 0L));
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, initialScanTimeInMs);
    // expectedThrottleCount + 1 because the next scan already started and the throttle count is incremented
    assertEquals("Throttle count mismatch from expected value", expectedThrottleCount + 1,
        mockThrottler.throttleCount.get());
    // request something outside of the forecast coverage
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(0L, 0L));
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, 0L);
    // resume the scan and make a request that will wait for the scan to complete
    scanHoldLatch.countDown();
    verifyContainerStorageStatsAndGetTotalValidSize(blobStoreStats, state.time.milliseconds());
    verifyAndGetLogSegmentValidSize(blobStoreStats, new TimeRange(state.time.milliseconds(), 0L));
    // first two are from the two bucketing scans and the later three are from the requests that are outside of
    // forecast coverage
    expectedThrottleCount = (2 + 3) * expectedThrottleCount;
    assertEquals("Throttle count mismatch from expected value", expectedThrottleCount,
        mockThrottler.throttleCount.get());
    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