private CompletableFuture<Boolean> deleteAsyncIfNeeded(BlobId blobId, long deletionTime, short lifeVersion) {
    String blobKey = blobId.getID();
    // Note: always check cache before operation attempt, since this could be a retry after a CONFLICT error,
    // in which case the cache may have been updated by another thread.
    if (!checkCacheState(blobKey, lifeVersion, BlobState.DELETED)) {
      return cloudDestination.deleteBlobAsync(blobId, deletionTime, lifeVersion, this::preDeleteValidation)
          .whenComplete((isDeleted, throwable) -> {
            if (throwable != null) {
              // Cache entry could be stale, evict it to force refresh on retry.
              removeFromCache(blobKey);
            } else {
              addToCache(blobKey, lifeVersion, BlobState.DELETED);
            }
          });
    }
    // This means that we definitely saw this delete for the same or smaller life version before.
    return FutureUtils.completedExceptionally(new CloudStorageException("Error updating blob metadata",
        new StoreException("Cannot delete id " + blobId.getID() + " since it is already marked as deleted in cloud.",
            StoreErrorCodes.IDDeleted)));
  }
Delete the specified blob if needed depending on the cache state asynchronously.
@param blobId the blob to delete
@param deletionTime the deletion time
@param lifeVersion life version of the blob.
@return a {@link CompletableFuture} that will eventually be {@code True} if the deletion was performed. If there was
        an exception during the delete, it will contain the related {@link CloudStorageException}.
ambry-cloud/src/main/java/com/github/ambry/cloud/CloudBlobStore.java