public void applyUpdatesToBlobInLocalStore(MessageInfo messageInfo, RemoteReplicaInfo remoteReplicaInfo,
      BlobId localKey) throws StoreException {
    MessageInfo localMessageInfo = remoteReplicaInfo.getLocalStore().findKey(localKey);
    boolean deletedLocally = localMessageInfo.isDeleted();
    boolean ttlUpdatedLocally = localMessageInfo.isTtlUpdated();
    short localLifeVersion = localMessageInfo.getLifeVersion();
    short remoteLifeVersion = messageInfo.getLifeVersion();
    if (localLifeVersion == remoteLifeVersion) {
      // we are operating in the same version, in this case, delete would be the final state.
      if (!deletedLocally) {
        // Only adds record when it's not deleted yet. Since delete is the final state for this lifeVersion, if there
        // is a delete record for the current lifeVersion, then nothing needs to be done.
        MessageInfo info = new MessageInfo.Builder(messageInfo).storeKey(localKey)
            .size(0)
            .accountId(localKey.getAccountId())
            .containerId(localKey.getContainerId())
            .build();
        if (messageInfo.isTtlUpdated() && !ttlUpdatedLocally) {
          applyTtlUpdate(info, remoteReplicaInfo);
        }
        if (messageInfo.isDeleted()) {
          applyDelete(info, remoteReplicaInfo);
        }
      }
    } else if (localLifeVersion < remoteLifeVersion) {
      // If we are here, then the remote lifeVersion is greater than the local lifeVersion.
      // If the local lifeVersion is greater than the remote lifeVersion, then there is nothing to be done.
      // we need to reconcile the local state with the remote state.
      //
      // There are three states we have to reconcile: lifeVersion, ttl_update, is_deleted.
      // To reconcile lifeVersion and is_deleted, we have to add a Delete or Undelete record, based on what the final state is.
      // to reconcile ttl_update, if the final state is delete, then, we have to add ttl_update before delete, other, we can add ttl_update after undelete.
      MessageInfo info = new MessageInfo(localKey, 0, localKey.getAccountId(), localKey.getContainerId(),
          messageInfo.getOperationTimeMs(), remoteLifeVersion);
      boolean shouldInsertTtlUpdate = false;
      if (messageInfo.isTtlUpdated() && !ttlUpdatedLocally) {
        // make a patch for ttl update
        // if the remote state is delete, then we can't insert TTL_UPDATE after delete, we have to insert a ttl_update here
        if (messageInfo.isDeleted()) {
          // since ttl update can only follow Put or Undelete, make sure it's not locally deleted.
          // we can reuse the lifeVersion for undelete and ttl update, since the delete would be the final state of
          // this lifeVersion.
          if (deletedLocally) {
            applyUndelete(info, remoteReplicaInfo);
          }
          applyTtlUpdate(info, remoteReplicaInfo);
        } else {
          // if final state is not delete, then to bump lifeVersion in local store to remote lifeVersion, we have to
          // add a undelete, and then add a ttl update.
          shouldInsertTtlUpdate = true;
        }
      }

      // if we are here, then the ttl update is matched
      if (messageInfo.isDeleted()) {
        applyDelete(info, remoteReplicaInfo);
      } else {
        applyUndelete(info, remoteReplicaInfo);
        if (shouldInsertTtlUpdate) {
          applyTtlUpdate(info, remoteReplicaInfo);
        }
      }
    }
  }
Compares blob metadata in received message from remote replica with the blob in local store and updates its
ttl_update/delete/undelete properties. This is called when a replicated blob from remote replica is found on local store.
@param messageInfo message information of the blob from remote replica
@param remoteReplicaInfo remote replica information
@param localKey local blob information
@throws StoreException
ambry-replication/src/main/java/com/github/ambry/replication/ReplicaThread.java