private void onComplete(PutOperation op) {
    Exception e = op.getOperationException();
    String blobId = op.getBlobIdString();
    op.maybeNotifyForBlobCreation();
    if (blobId == null && e == null) {
      e = new RouterException("Operation failed, but exception was not set", RouterErrorCode.UnexpectedInternalError);
      routerMetrics.operationFailureWithUnsetExceptionCount.inc();
    }
    if (e != null) {
      op.cleanupChunks();
      blobId = null;
      routerMetrics.onPutBlobError(e, op.isEncryptionEnabled(), op.isStitchOperation());
      routerCallback.scheduleDeletes(op.getSuccessfullyPutChunkIdsIfCompositeDirectUpload(), op.getServiceId(),
          op.getQuotaChargeCallback());
    } else {
      updateChunkingAndSizeMetricsOnSuccessfulPut(op);
    }
    routerMetrics.operationDequeuingRate.mark();
    long operationLatencyMs = time.milliseconds() - op.getSubmissionTimeMs();
    if (op.isStitchOperation()) {
      (op.isEncryptionEnabled() ? routerMetrics.stitchEncryptedBlobOperationLatencyMs
          : routerMetrics.stitchBlobOperationLatencyMs).update(operationLatencyMs);
    } else {
      (op.isEncryptionEnabled() ? routerMetrics.putEncryptedBlobOperationLatencyMs
          : routerMetrics.putBlobOperationLatencyMs).update(operationLatencyMs);
    }
    // Preemptively clean up of correlation IDs to avoid retaining a reference to the PutOperation for longer than
    // required. We do not care about any other
    for (Integer correlationId : op.getInFlightCorrelationIds()) {
      correlationIdToPutOperation.remove(correlationId);
    }
    // Regardless of the result of the operation, clean up the blobs that may have been put as the result of slipped
    // puts.
    routerCallback.scheduleDeletes(Lists.newArrayList(op.getSlippedPutBlobIds()), op.getServiceId(),
        op.getQuotaChargeCallback());
    nonBlockingRouter.completeOperation(op.getFuture(), op.getCallback(), blobId, e);
  }
Called for a {@link PutOperation} when the operation is complete. Any cleanup that the PutManager needs to do
with respect to this operation will have to be done here. The PutManager also finishes the operation by
performing the callback and notification.
@param op the {@link PutOperation} that has completed.
ambry-router/src/main/java/com/github/ambry/router/PutManager.java