protected void updateBlobTtl(final String blobIdStr, final String serviceId, long expiresAtMs,
      FutureResult<Void> futureResult, Callback<Void> callback, QuotaChargeCallback quotaChargeCallback) {
    doOperationTowardsMaybeCompositeBlob(blobIdStr, futureResult, callback,
        new CompositeBlobOperationHelper("TTL UPDATE", GetOption.None, routerMetrics.ageAtTtlUpdate,
            (blobId, chunkIds) -> {
              doUpdateTtlOperation(blobId, chunkIds, serviceId, expiresAtMs, futureResult, callback,
                  quotaChargeCallback);
            }, (blobInfo -> isBlobTtlUpdated(blobInfo)), (exception) -> {
          nonBlockingRouter.completeUpdateBlobTtlOperation(exception, futureResult, callback);
        }), quotaChargeCallback);
  }
Requests for the ttl of a blob to be updated asynchronously and invokes the {@link Callback} when the request
completes.
@param blobIdStr The ID of the blob that needs the ttl update in string form
@param serviceId The service ID of the service updating the ttl of the blob. This can be null if unknown.
@param expiresAtMs The new expiry time (in ms) of the blob. Using {@link Utils#Infinite_Time} makes the blob
                   permanent
@param futureResult A future that would contain the BlobId eventually.
@param callback The {@link Callback} which will be invoked on the completion of the request .
@param quotaChargeCallback The {@link QuotaChargeCallback} object.
ambry-router/src/main/java/com/github/ambry/router/OperationController.java