public CompletableFuture<CloudBlobMetadata> getBlobMetadataAsync(BlobId blobId) {
    return FutureUtils.orTimeout(storageClient.getPropertiesWithResponse(blobId, defaultRequestConditions),
        requestTimeout).handle((properties, throwable) -> {
      if (throwable != null) {
        Exception ex = Utils.extractFutureExceptionCause(throwable);
        if (ex instanceof BlobStorageException && isNotFoundError(((BlobStorageException) ex).getErrorCode())) {
          // Received exception for blob not found.
          logger.debug("Blob {} not found.", blobId);
          return null;
        }
        throw throwable instanceof CompletionException ? (CompletionException) throwable
            : new CompletionException(throwable);
      }
      if (properties == null) {
        // If blob properties is null, return null
        logger.debug("Blob {} not found.", blobId);
        return null;
      }
      return CloudBlobMetadata.fromMap(properties.getMetadata());
    });
  }
Retrieve the metadata for the specified blob asynchronously.
@param blobId The {@link BlobId} to retrieve.
@return a CompletableFuture that will eventually contain the {@link CloudBlobMetadata} if the blob
        was found, or null otherwise. If there was an error on ABS side, it will complete exceptionally containing
        the BlobStorageException.
ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureBlobDataAccessor.java