@Override
  public void handleTtlUpdateRequest(NetworkRequest request) throws IOException, InterruptedException {
    TtlUpdateRequest updateRequest;
    if (request instanceof LocalChannelRequest) {
      // This is a case where handleTtlUpdateRequest is called when frontends are talking to Azure. In this case, this method
      // is called by request handler threads running within the frontend router itself. So, the request can be directly
      // referenced as java objects without any need for deserialization.
      updateRequest = (TtlUpdateRequest) ((LocalChannelRequest) request).getRequestInfo().getRequest();
    } else {
      updateRequest = TtlUpdateRequest.readFrom(new DataInputStream(request.getInputStream()), clusterMap);
    }
    long requestQueueTime = SystemTime.getInstance().milliseconds() - request.getStartTimeInMs();
    long totalTimeSpent = requestQueueTime;
    long startTime = SystemTime.getInstance().milliseconds();
    TtlUpdateResponse response = null;
    try {
      ServerErrorCode error =
          validateRequest(updateRequest.getBlobId().getPartition(), RequestOrResponseType.TtlUpdateRequest, false);
      if (error != ServerErrorCode.NoError) {
        logger.error("Validating TtlUpdateRequest failed with error {} for request {}", error, updateRequest);
        response = new TtlUpdateResponse(updateRequest.getCorrelationId(), updateRequest.getClientId(), error);
      } else {
        BlobId convertedStoreKey =
            (BlobId) getConvertedStoreKeys(Collections.singletonList(updateRequest.getBlobId())).get(0);
        MessageInfo info = new MessageInfo.Builder(convertedStoreKey, -1, convertedStoreKey.getAccountId(),
            convertedStoreKey.getContainerId(), updateRequest.getOperationTimeInMs()).isTtlUpdated(true)
            .expirationTimeInMs(updateRequest.getExpiresAtMs())
            .lifeVersion(MessageInfo.LIFE_VERSION_FROM_FRONTEND)
            .build();
        Store store = storeManager.getStore(updateRequest.getBlobId().getPartition());
        store.updateTtl(Collections.singletonList(info));
        response = new TtlUpdateResponse(updateRequest.getCorrelationId(), updateRequest.getClientId(),
            ServerErrorCode.NoError);
        if (notification != null) {
          notification.onBlobReplicaUpdated(currentNode.getHostname(), currentNode.getPort(), convertedStoreKey.getID(),
              BlobReplicaSourceType.PRIMARY, UpdateType.TTL_UPDATE, info);
        }
      }
    } catch (StoreException e) {
      boolean logInErrorLevel = false;
      if (e.getErrorCode() == StoreErrorCodes.IDNotFound) {
        metrics.idNotFoundError.inc();
      } else if (e.getErrorCode() == StoreErrorCodes.TTLExpired) {
        metrics.ttlExpiredError.inc();
      } else if (e.getErrorCode() == StoreErrorCodes.IDDeleted) {
        metrics.idDeletedError.inc();
      } else if (e.getErrorCode() == StoreErrorCodes.AuthorizationFailure) {
        metrics.ttlUpdateAuthorizationFailure.inc();
      } else if (e.getErrorCode() == StoreErrorCodes.AlreadyUpdated) {
        metrics.ttlAlreadyUpdatedError.inc();
      } else if (e.getErrorCode() == StoreErrorCodes.UpdateNotAllowed) {
        metrics.ttlUpdateRejectedError.inc();
      } else {
        logInErrorLevel = true;
        metrics.unExpectedStoreTtlUpdateError.inc();
      }
      if (logInErrorLevel) {
        logger.error("Store exception on a TTL update with error code {} for request {}", e.getErrorCode(),
            updateRequest, e);
      } else {
        logger.trace("Store exception on a TTL update with error code {} for request {}", e.getErrorCode(),
            updateRequest, e);
      }
      response = new TtlUpdateResponse(updateRequest.getCorrelationId(), updateRequest.getClientId(),
          ErrorMapping.getStoreErrorMapping(e.getErrorCode()));
    } catch (Exception e) {
      logger.error("Unknown exception for TTL update request {}", updateRequest, e);
      response = new TtlUpdateResponse(updateRequest.getCorrelationId(), updateRequest.getClientId(),
          ServerErrorCode.UnknownError);
      metrics.unExpectedStoreTtlUpdateError.inc();
    } finally {
      long processingTime = SystemTime.getInstance().milliseconds() - startTime;
      totalTimeSpent += processingTime;
      publicAccessLogger.info("{} {} processingTime {}", updateRequest, response, processingTime);
      metrics.updateBlobTtlProcessingTimeInMs.update(processingTime);
      // Update request metrics.
      RequestMetricsUpdater metricsUpdater = new RequestMetricsUpdater(requestQueueTime, processingTime, 0, 0, false);
      updateRequest.accept(metricsUpdater);
    }
    requestResponseChannel.sendResponse(response, request,
        new ServerNetworkResponseMetrics(metrics.updateBlobTtlResponseQueueTimeInMs, metrics.updateBlobTtlSendTimeInMs,
            metrics.updateBlobTtlTotalTimeInMs, null, null, totalTimeSpent));
  }
Handler for all metrics to be updated for batch delete operation.
ambry-server/src/main/java/com/github/ambry/server/AmbryRequests.java