private void performHardDeletes(List<MessageInfo> messageInfoList) throws StoreException {
    try {
      EnumSet<StoreGetOptions> getOptions = EnumSet.of(StoreGetOptions.Store_Include_Deleted);
      List<BlobReadOptions> readOptionsList = new ArrayList<BlobReadOptions>(messageInfoList.size());

      /* First create the readOptionsList */
      for (MessageInfo info : messageInfoList) {
        if (!enabled.get()) {
          throw new StoreException("Aborting, store is shutting down", StoreErrorCodes.StoreShuttingDown);
        }
        try {
          BlobReadOptions readInfo = index.getBlobReadInfo(info.getStoreKey(), getOptions);
          readOptionsList.add(readInfo);
        } catch (StoreException e) {
          logger.debug("Failed to read blob info for blobid {} during hard deletes, ignoring. Caught exception {}",
              info.getStoreKey(), e);
          metrics.hardDeleteExceptionsCount.inc();
        }
      }

      List<LogWriteInfo> logWriteInfoList = new ArrayList<LogWriteInfo>();

      StoreMessageReadSet readSet = new StoreMessageReadSet(readOptionsList);
      Iterator<HardDeleteInfo> hardDeleteIterator = hardDelete.getHardDeleteMessages(readSet, factory, null);
      Iterator<BlobReadOptions> readOptionsIterator = readOptionsList.iterator();

      persistFileLock.lock();
      try {
        /* Next, get the information to persist hard delete recovery info. Get all the information and save it, as only
         * after the whole range is persisted can we start with the actual log write */
        while (hardDeleteIterator.hasNext()) {
          if (!enabled.get()) {
            throw new StoreException("Aborting hard deletes as store is shutting down",
                StoreErrorCodes.StoreShuttingDown);
          }
          HardDeleteInfo hardDeleteInfo = hardDeleteIterator.next();
          BlobReadOptions readOptions = readOptionsIterator.next();
          if (hardDeleteInfo == null) {
            metrics.hardDeleteFailedCount.inc(1);
          } else {
            hardDeleteRecoveryRange.addMessageInfo(readOptions, hardDeleteInfo.getRecoveryInfo(),
                (StoreFindToken) startToken);
            LogSegment logSegment = log.getSegment(readOptions.getLogSegmentName());
            logWriteInfoList.add(new LogWriteInfo(logSegment, hardDeleteInfo.getHardDeleteChannel(),
                readOptions.getOffset() + hardDeleteInfo.getStartOffsetInMessage(),
                hardDeleteInfo.getHardDeletedMessageSize()));
          }
        }

        if (readOptionsIterator.hasNext()) {
          metrics.hardDeleteExceptionsCount.inc(1);
          throw new IllegalStateException("More number of blobReadOptions than hardDeleteMessages");
        }

        // PersistCleanupToken because EndToken and the RecoveryRange changed.
        persistCleanupToken();
      } finally {
        persistFileLock.unlock();
      }

      /* Finally, write the hard delete stream into the Log */
      for (LogWriteInfo logWriteInfo : logWriteInfoList) {
        if (!enabled.get()) {
          throw new StoreException("Aborting hard deletes as store is shutting down",
              StoreErrorCodes.StoreShuttingDown);
        }
        diskIOScheduler.getSlice(HARD_DELETE_CLEANUP_JOB_NAME, HARD_DELETE_CLEANUP_JOB_NAME, logWriteInfo.size);
        logWriteInfo.logSegment.writeFrom(logWriteInfo.channel, logWriteInfo.offset, logWriteInfo.size);
        metrics.hardDeleteDoneCount.inc(1);
      }
    } catch (IOException e) {
      StoreErrorCodes errorCode = StoreException.resolveErrorCode(e);
      throw new StoreException(errorCode.toString() + " while performing hard delete ", e, errorCode);
    }
    logger.trace("Performed hard deletes from {} to {} for {}", startToken, endToken, dataDir);
  }
Performs hard deletes of all the messages in the messageInfoList.
Gets a view of the records in the log for those messages and calls cleanup to get the appropriate replacement
records, and then replaces the records in the log with the corresponding replacement records.
@param messageInfoList: The messages to be hard deleted in the log.
ambry-store/src/main/java/com/github/ambry/store/HardDeleter.java