protected MessageInfo mapBlob(MessageInfo blob, List<StoreKey> storeKeysConversionLog) {
    StoreKey keyConvert = null;
    try {
      // Don't do batch-convert, if one replica in batch fails, then it affects handling others
      keyConvert = convertStoreKeys(Collections.singleton(blob.getStoreKey()), storeKeysConversionLog)
          .get(blob.getStoreKey());
    } catch (Throwable e) {
      metrics.backupIntegrityError.inc();
      logger.error("Failed to convert blobID {} due to ", blob.getStoreKey().getID(), e.toString());
    }
    return new MessageInfo(keyConvert, blob);
  }
Instead of over-riding applyPut, it is better to override the {@link #afterHandleReplicaMetadataResponse}.
We already know the missing keys. We know they are not deleted or expired on the remote node.
There is no benefit in fetching the entire blob only to discard it, unless we are doing a data comparison also.
That's why we set the state of this group to be DONE.
TODO: We should probably fetch just checksums if we want to compare blob-content in the future.
ambry-replication/src/main/java/com/github/ambry/replication/BackupCheckerThread.java