void checkAndMaybeComplete() {
      boolean done = false;
      // Now, check if this chunk is done.
      if (chunkException != null && chunkException.getErrorCode() == RouterErrorCode.BlobCorrupted) {
        logger.error("{} : Put chunk {} failed due to corruption. Failing entire operation", loggingContext,
            getChunkBlobId());
        // Append this blob to slipped put so that it is cleaned up later. If this is a composite blob, the rest of the
        // chunks will be cleaned up automatically as part of PUT operation clean up in PutManager#onComplete()
        appendSlippedPutBlobId(chunkBlobId);
        setOperationExceptionAndComplete(chunkException);
        return;
      }

      if (operationTracker.isDone() || (chunkException != null
          && chunkException.getErrorCode() == RouterErrorCode.TooManyRequests)) {
        if (!operationTracker.hasSucceeded()) {
          failedAttempts++;
          appendSlippedPutBlobId(chunkBlobId);
          if (failedAttempts <= routerConfig.routerMaxSlippedPutAttempts && !shouldUseReservedMetadataId()) {
            logger.trace("{}: Attempt to put chunk with id: {} failed, attempting slipped put ", loggingContext,
                chunkBlobId);
            routerMetrics.slippedPutAttemptCount.inc();
            if (this instanceof MetadataPutChunk) {
              routerMetrics.metadataSlippedPutAttemptCount.inc();
            }
            chunkException = null;
            prepareForSending();
          } else {
            // this chunk could not be successfully put. The whole operation has to fail.
            logger.error("{}: Slipped puts failed for {}", loggingContext, getChunkBlobId());
            chunkBlobId = null;
            setOperationExceptionAndComplete(chunkException);
            done = true;
          }
        } else {
          chunkException = null;
          done = true;
        }
      }
      if (done) {
        // the chunk is complete now. We can charge against quota for the chunk if its not a metadata chunk.
        if (QuotaUtils.postProcessCharge(quotaChargeCallback) && !(this instanceof MetadataPutChunk)
            && chunkException == null) {
          try {
            quotaChargeCallback.checkAndCharge(false, true, chunkBlobProperties.getBlobSize());
          } catch (QuotaException quotaException) {
            // For now we only log for quota charge exceptions for in progress requests.
            logger.info("{}: Exception {} while handling quota charge event", loggingContext,
                quotaException.toString());
          }
        }
        if (this instanceof MetadataPutChunk && chunkException == null) {
          // if this is a metadata chunk, and it was successful then increment the count of metadata chunk creation.
          routerMetrics.metadataChunkCreationCount.inc();
        }
        state = ChunkState.Complete;
      }
    }
Check if the operation on the chunk is eligible for completion, if so complete it.
ambry-router/src/main/java/com/github/ambry/router/PutOperation.java