@Override
  public Future<Void> deleteBlob(RestRequest restRequest, String blobId, String serviceId, Callback<Void> callback,
      QuotaChargeCallback quotaChargeCallback) {
    FutureResult<Void> futureResult = new FutureResult<>();
    if (restRequest == null) {
      if (blobId == null) {
        throw new IllegalArgumentException("blobId must not be null");
      }
      proceedWithDelete(blobId, serviceId, callback, futureResult, quotaChargeCallback);
    } else {
      try {
        String blobIdStr = getRequestPath(restRequest).getOperationOrBlobId(true);

        // Convert asynchronously and proceed once blobId is available
        idConverter.convert(restRequest, blobIdStr, null, new Callback<String>() {
          @Override
          public void onCompletion(String convertedBlobId, Exception exception) {
            if (exception != null) {
              callback.onCompletion(null, exception);
            } else {
              List<String> blobIds = Arrays.stream(convertedBlobId.split(",")).collect(Collectors.toList());
              if (blobIds.size() == 1) {
                proceedWithDelete(blobIds.get(0), serviceId, callback, futureResult, quotaChargeCallback);
              } else {
                Function<Exception, Exception> allowNotFound = ex -> ex != null && ex instanceof RouterException
                    && ((RouterException) ex).getErrorCode() == RouterErrorCode.BlobDoesNotExist ? null : ex;

                BatchOperationCallbackTracker tracker =
                    new BatchOperationCallbackTracker(blobIds, futureResult, callback, quotaChargeCallback,
                        allowNotFound, NonBlockingRouter.this);
                for (String blobId : blobIds) {
                  proceedWithDelete(blobId, serviceId, tracker.getCallback(blobId),
                      BatchOperationCallbackTracker.DUMMY_FUTURE, quotaChargeCallback);
                }
              }
            }
          }
        });
        return futureResult; // Return early since we're waiting for the async operation
      } catch (Exception e) {
        callback.onCompletion(null, e);
        return futureResult;
      }
    }
    return futureResult;
  }
Requests for a blob to be deleted asynchronously and invokes the {@link Callback} when the request completes.

@param restRequest The {@link RestRequest} to delete the blob.
@param blobId      The ID of the blob that needs to be deleted.
@param serviceId   The service ID of the service deleting the blob. This can be null if unknown.
@param callback    The {@link Callback} which will be invoked on the completion of a request.
@return A future that would contain information about whether the deletion succeeded or not, eventually.
ambry-router/src/main/java/com/github/ambry/router/NonBlockingRouter.java