protected void maybeProcessCallbacks() {
      if (!isOperationComplete() && progressTracker.isCryptoJobRequired()
          && decryptCallbackResultInfo.decryptJobComplete) {
        logger.trace("Processing decrypt callback stored result for data chunk {}", chunkBlobId);
        decryptJobMetricsTracker.onJobResultProcessingStart();
        // Only when the blob is encrypted should we need to call this method. When finish decryption, we don't need
        // response info anymore.
        if (decryptCallbackResultInfo.exception == null) {
          // Nullify result so we know we already passed the bytebuf to chunkIndexToBuf map
          DecryptJob.DecryptJobResult result = decryptCallbackResultInfo.result.getAndSet(null);
          if (result != null) {
            if (chunkIndex == 0) {
              routerMetrics.getFirstDataChunkLatencyMs.update(System.currentTimeMillis() - submissionTimeMs);
            }
            routerMetrics.getDataChunkLatencyMs.update(System.currentTimeMillis() - initializedTimeMs);
            ByteBuf decryptedContent = result.getDecryptedBlobContent();
            ByteBuf decompressedContent = decompressContent(decryptedContent);
            if (decompressedContent != null) {
              chunkIndexToBuf.put(chunkIndex, filterChunkToRange(decompressedContent));
              numChunksRetrieved.incrementAndGet();
            } else {
              progressTracker.setCryptoJobFailed();
            }
          } else {
            // only in maybeReleaseDecryptionResultBuffer() will result be set to null, this means the bytebuf has been
            // released, don't have to release it again here.
          }
          logger.trace("Decrypt result successfully updated for data chunk {}", chunkBlobId);
          progressTracker.setCryptoJobSuccess();
        } else {
          decryptJobMetricsTracker.incrementOperationError();
          logger.trace("Setting operation exception as decryption callback invoked with exception {} for data chunk {}",
              decryptCallbackResultInfo.exception, chunkBlobId);
          setOperationException(buildChunkException("Exception thrown on decrypting the content for data chunk",
              decryptCallbackResultInfo.exception, RouterErrorCode.UnexpectedInternalError));
          progressTracker.setCryptoJobFailed();
        }
        decryptJobMetricsTracker.onJobResultProcessingComplete();
        logger.trace("Marking blob content available to process for data chunk {}", chunkBlobId);
      }
    }
Maybe process callbacks if applicable. This is a no-op for blobs that do not need any async processing.
As of now, decryption is the only async processing that could happen if applicable.
ambry-router/src/main/java/com/github/ambry/router/GetBlobOperation.java