private void processIndexSegmentEntriesBackward(IndexSegment indexSegment, Predicate<IndexEntry> predicate,
        Map<StoreKey, IndexFinalState> keyFinalStates) throws StoreException {
      diskIOScheduler.getSlice(BlobStoreStats.IO_SCHEDULER_JOB_TYPE, BlobStoreStats.IO_SCHEDULER_JOB_ID,
          indexSegment.size());
      logger.trace("Processing index entries backward by IndexScanner for segment {} for store {}",
          indexSegment.getFile().getName(), storeId);

      // valid index entries wrt log segment reference time
      forEachValidIndexEntry(indexSegment, newScanResults.logSegmentForecastStartTimeMsForDeleted,
          newScanResults.logSegmentForecastStartTimeMsForExpired, null, keyFinalStates, false, entry -> {
            if (predicate == null || predicate.test(entry)) {
              processEntryForLogSegmentBucket(newScanResults, entry, keyFinalStates);
            }
          });
      // valid index entries wrt container reference time
      forEachIndexEntry(indexSegment, newScanResults.containerForecastStartTimeMs,
          newScanResults.containerForecastStartTimeMs, null, keyFinalStates, true, (entry, isValid) -> {
            if (predicate == null || predicate.test(entry)) {
              processEntryForContainerBucket(newScanResults, entry, isValid, keyFinalStates);
            }
          });
    }
Process a {@link List} of {@link IndexEntry} belonging to the same {@link IndexSegment} to populate container and
log segment buckets. The function is called in reverse chronological order. That is, newest {@link IndexSegment}
to older ones.
@param indexSegment the {@link IndexSegment} where the index entries belong to
@param predicate if not null, then only apply entries from the index segment if the predicate is true.
@param keyFinalStates a {@link Map} of key to {@link IndexFinalState}.
@throws StoreException
ambry-store/src/main/java/com/github/ambry/store/BlobStoreStats.java