private StoreFindToken generateTokenBasedOnIndexSegmentOffset(StoreFindToken token,
      ConcurrentSkipListMap<Offset, IndexSegment> indexSegments) throws StoreException {
    metrics.generateTokenBasedOnCompactionHistoryCount.inc();
    logger.trace("Index {}: Generating token based on index segment offset for token {}", dataDir, token);
    if (token.getType() == FindTokenType.Uninitialized) {
      throw new IllegalArgumentException("Index" + dataDir + ": Invalid token: " + token);
    }
    if (indexSegments.get(token.getOffset()) != null) {
      throw new IllegalArgumentException(
          "Index " + dataDir + ": Token's offset already exist in the index segment map: " + token);
    }
    boolean isIndexSegmentOffset = token.getType().equals(FindTokenType.IndexBased);
    Offset offset = token.getOffset();
    IndexSegment segment;
    while (true) {
      Offset afterOffset;
      if (isIndexSegmentOffset) {
        afterOffset = beforeAndAfterCompactionIndexSegmentOffsets.get(offset);
      } else {
        // This map only contains the index segments' start offsets, it doesn't have offsets from journal based token.
        // Lucky for us, offset in journal based token is the index entry's offset and the index entry's offset should
        // always be greater than or equal to the start offset of the index segment, which this index entry belongs to.
        // And this index segment's start offset should be saved in this map.
        Offset start = new Offset(offset.getName(), 0);
        Offset end = new Offset(offset.getName(), config.storeSegmentSizeInBytes + 1);
        afterOffset = beforeAndAfterCompactionIndexSegmentOffsets.subMap(start, false, end, false).floorKey(offset);
        if (afterOffset == null || !afterOffset.getName().equals(offset.getName())) {
          // LogSegmentName check is for sanity, we already have a start and end offset to limit the range for offset.
          logger.info("Index {}: Non-IndexSegment Offset {} can't find its index segment offset before compaction: {}",
              dataDir, offset, afterOffset);
          return new StoreFindToken();
        }
        isIndexSegmentOffset = true;
      }
      if (afterOffset == null) {
        logger.info(
            "Index {}: Offset {} is not found in compaction index segment offset maps, token is reset to the beginning of whole log",
            dataDir, offset);
        return new StoreFindToken();
      }
      segment = indexSegments.get(afterOffset);
      if (segment != null) {
        logger.trace("Index {}: Offset {} maps to {} after compaction, reset token to this index segment", dataDir,
            offset, afterOffset);
        metrics.offsetFoundInBeforeAfterMapCount.inc();
        return new StoreFindToken(FindTokenType.IndexBased, segment.getStartOffset(),
            segment.getFirstKeyInSealedSegment(), sessionId, incarnationId, true, StoreFindToken.CURRENT_VERSION,
            segment.getResetKey(), segment.getResetKeyType(), segment.getResetKeyLifeVersion());
      } else {
        logger.trace("Index {}: Offset {} maps to {} after compaction, but it doesn't exist in index", dataDir, offset,
            afterOffset);
      }
      offset = afterOffset;
    }
  }
Generate new {@link StoreFindToken} based on the index segment offset information before and after compaction.
If the input token is an index based token, then this method would try to find the offset of after compaction index
segment for its offset. It will return the index segment after compaction (referenced by offset) as the StoreFindToken
if this method finds it, otherwise, it would return an uninitialized token.
If the input token is a journal based token, which means its offset doesn't show up in the offset information before
and after compaction. Then first this method will do is to find the offset of the before-compaction index segment
this journal token belongs to. If it can find the offset, it will then do the same thing as the index based token.
Otherwise it will return an uninitialized token.
@param token The token whose offset is now missing from the index segment map.
@param indexSegments The current index segment map.
@return A new {@link StoreFindToken} that is guaranteed to be safe for replication.
@throws StoreException
ambry-store/src/main/java/com/github/ambry/store/PersistentIndex.java