@Override
  public short undelete(MessageInfo info) throws StoreException {
    StoreKey key = info.getStoreKey();
    MessageInfo deleteInfo = getMessageInfo(key, messageInfos, true, false, false);
    if (info.getLifeVersion() == -1 && deleteInfo == null) {
      throw new StoreException("Key " + key + " not delete yet", StoreErrorCodes.IDNotDeleted);
    }
    short lifeVersion = info.getLifeVersion();
    MessageInfo latestInfo = deleteInfo;
    if (info.getLifeVersion() == MessageInfo.LIFE_VERSION_FROM_FRONTEND) {
      if (deleteInfo == null) {
        throw new StoreException("Id " + key + " requires first value to be a put and last value to be a delete",
            StoreErrorCodes.IDNotDeleted);
      }
      lifeVersion = (short) (deleteInfo.getLifeVersion() + 1);
    } else {
      if (deleteInfo == null) {
        latestInfo = getMergedMessageInfo(key, messageInfos);
      }
    }
    try {
      MessageFormatInputStream stream =
          new UndeleteMessageFormatInputStream(key, info.getAccountId(), info.getContainerId(),
              info.getOperationTimeMs(), lifeVersion);
      // Update info to add stream size;
      info = new MessageInfo(key, stream.getSize(), false, latestInfo.isTtlUpdated(), true,
          latestInfo.getExpirationTimeInMs(), null, info.getAccountId(), info.getContainerId(),
          info.getOperationTimeMs(), lifeVersion);
      MessageFormatWriteSet writeSet = new MessageFormatWriteSet(stream, Collections.singletonList(info), false);
      writeSet.writeTo(log);
      messageInfos.add(info);
      return lifeVersion;
    } catch (Exception e) {
      throw new StoreException("Unknown error while trying to undelete blobs from store", e,
          StoreErrorCodes.UnknownError);
    }
  }
Log that stores all data in memory.
ambry-replication/src/test/java/com/github/ambry/replication/InMemoryStore.java