void validateSanityForUndelete(StoreKey key, List<IndexValue> values, short lifeVersion) throws StoreException {
    if (values == null || values.isEmpty()) {
      throw new StoreException("Id " + key + " not found in " + dataDir, StoreErrorCodes.IDNotFound);
    }
    if (!IndexValue.hasLifeVersion(lifeVersion)) {
      validateSanityForUndeleteWithoutLifeVersion(key, values);
      return;
    }
    // This is from replication. For replication, undelete should be permitted only when
    // 1. The oldest record is PUT
    // 2. the latest record's lifeVersion is less then undelete's lifeVersion.
    // 3. Replication doesn't care if the latest record is delete or not. In local, we can have a PUT record when
    // the remote has PUT, DELETE, UNDELETE. When replicating from remote, local has to insert UNDELETE, where
    // there is no prior DELETE.
    // 4. Replication doesn't care if the latest record is expired or not. In local, we can have a PUT record when
    // the remote has PUT, DELETE, UNDELETE TTL_UPDATE. When replicating from remote, PUT might already have expired.
    // But later we will apply TTL_UPDATE. The alternative idea is to change the logic of checking expiration from
    // comparing current time to comparing operation time.
    IndexValue latestValue = values.get(0);
    IndexValue oldestValue = values.get(values.size() - 1);
    if (!oldestValue.isPut()) {
      throw new StoreException("Id " + key + " requires first value to be a put in index " + dataDir,
          StoreErrorCodes.IDDeletedPermanently);
    }
    if (latestValue.getLifeVersion() == lifeVersion && latestValue.isUndelete()) {
      throw new IdUndeletedStoreException(
          "Id " + key + " already undeleted at lifeVersion " + lifeVersion + " in index " + dataDir, lifeVersion);
    }
    if (latestValue.getLifeVersion() >= lifeVersion) {
      throw new StoreException(
          "LifeVersion conflict in index. Id " + key + " LifeVersion: " + latestValue.getLifeVersion()
              + " Undelete LifeVersion: " + lifeVersion, StoreErrorCodes.LifeVersionConflict);
    }
  }
Ensure that the previous {@link IndexValue}s is structured correctly for undeleting the {@code key}.
@param key the key to be undeleted.
@param values the previous {@link IndexValue}s in reversed order.
@param lifeVersion lifeVersion for the undelete record, it's only valid when in recovery or replication.
ambry-store/src/main/java/com/github/ambry/store/PersistentIndex.java