private void verifyData() throws IOException, StoreException {
    LOGGER.info("Verifying data");
    IndexEntriesIterator srcEntriesIterator = new IndexEntriesIterator(srcIndex);
    IndexEntriesIterator tgtEntriesIterator = new IndexEntriesIterator(tgtIndex);

    while (srcEntriesIterator.hasNext()) {
      IndexEntry srcEntry = srcEntriesIterator.next();
      StoreKey key = srcEntry.getKey();
      IndexValue srcValue = srcEntry.getValue();
      String errMsgId = getErrorMessageId(srcEntry, srcIndex);

      boolean shouldVerifyRecord;
      boolean wasProcessedForCompaction = wasProcessedForCompaction(srcValue.getOffset());
      if (wasProcessedForCompaction) {
        if (!srcValue.isFlagSet(IndexValue.Flags.Delete_Index)) {
          // put entry
          IndexValue valueFromSrcIndex = srcIndex.findKey(key);
          IndexValue valueFromTgtIndex = tgtIndex.findKey(key);

          // expiry checks
          if (isExpiredAt(srcValue, compactionStartTimeMs)) {
            // put entry that had surely expired at compaction start time - so should have been cleaned up
            assert !isPutRecordPresentInTgt(key, valueFromTgtIndex) :
                errMsgId + ": Found an expired PUT record that should have been cleaned up in target index."
                    + " IndexVale: " + srcValue;
            shouldVerifyRecord = false;
          } else if (isExpiredAt(srcValue, compactionEndTimeMs)) {
            // put entry that had expired at end time but might not have been cleaned up because it wasn't expired
            // when examined
            shouldVerifyRecord = isPutRecordPresentInTgt(key, valueFromTgtIndex);
          } else {
            // not expired at compaction time
            shouldVerifyRecord = true;
          }
          if (shouldVerifyRecord && valueFromSrcIndex.isFlagSet(IndexValue.Flags.Delete_Index)) {
            // blob has been deleted. Find the time of the delete
            IndexSegment indexSegment =
                srcIndex.getIndexSegments().floorEntry(valueFromSrcIndex.getOffset()).getValue();
            long deleteTimeMs = valueFromSrcIndex.getOperationTimeInMs();
            if (deleteTimeMs == Utils.Infinite_Time) {
              deleteTimeMs = indexSegment.getLastModifiedTimeMs();
            }
            if (deleteRefTimeMs <= deleteTimeMs) {
              shouldVerifyRecord = true;
            } else if (indexSegment.getStartOffset().equals(srcIndex.getIndexSegments().lastKey())) {
              // if the delete lies in the last index segment of the srcIndex, then it is possible that the
              // last modified time of the index segment has changed in the tgtIndex (due to traffic). If this is the
              // case, it is possible that the PUT record has not been cleaned up depending on when compaction ran
              shouldVerifyRecord = isPutRecordPresentInTgt(key, valueFromTgtIndex);
            } else {
              // PUT record should no longer be present
              assert !isPutRecordPresentInTgt(key, valueFromTgtIndex) :
                  errMsgId + ": Found a deleted PUT record that should have been cleaned up in target index."
                      + " IndexVale: " + srcValue;
              shouldVerifyRecord = false;
            }
          }
          if (!config.checkAllEntriesInTgt && shouldVerifyRecord && valueFromTgtIndex.isFlagSet(
              IndexValue.Flags.Delete_Index)) {
            // deleted in the target index. Because the start offset of the index segment until which deletes are valid
            // is calculated before compaction, it is possible that the LMT of the index segment changed after this
            // calculation was done. This can happen only if there has been no write to the index segment for the past
            // 7 days and only if the tgt is expected to have more entries than the src.
            Offset startOffsetDelIdxSegInTgt = tgtIndex.getIndexSegments().floorKey(valueFromTgtIndex.getOffset());
            if (startOffsetDelIdxSegInTgt.equals(srcIndex.getIndexSegments().lastKey())) {
              // if the delete lies in the last index segment that tgtIndex has in common with srcIndex, then it is
              // possible that the last modified time of the index segment has changed in the tgtIndex (due to traffic).
              // If this is the case, it is possible that the PUT record has been cleaned up depending on when the start
              // offset for delete was calculated.
              shouldVerifyRecord = isPutRecordPresentInTgt(key, valueFromTgtIndex);
            }
          }
        } else {
          // delete entry. Should exist in the target log/index
          shouldVerifyRecord = true;
        }
      } else {
        // never underwent compaction. Needs to be checked.
        shouldVerifyRecord = true;
      }
      if (shouldVerifyRecord) {
        verifyRecord(errMsgId, srcEntry, tgtEntriesIterator, wasProcessedForCompaction || config.checkAllData);
      }
    }

    assert !config.checkAllEntriesInTgt
        || !tgtEntriesIterator.hasNext() : "There should be no more entries in the target index";
  }
Verifies the data in the store by walking through all the index entries in the source and verifying their
presence/absence in the target. Also implicitly verifies that ordering is unmodified.
@throws IOException if there is any I/O error.
@throws StoreException if there is any problem performing store operations.
ambry-tools/src/main/java/com/github/ambry/store/CompactionVerifier.java