public CompletableFuture<Void> putAsync(MessageWriteSet messageSetToWrite) {
    try {
      checkStarted();
      if (messageSetToWrite.getMessageSetInfo().isEmpty()) {
        throw new IllegalArgumentException("Message write set cannot be empty");
      }
      checkDuplicates(messageSetToWrite.getMessageSetInfo());

      CloudWriteChannel cloudWriter = new CloudWriteChannel(this, messageSetToWrite.getMessageSetInfo());
      // Write the blobs in the message set to cloud write channel asynchronously
      return ((CloudMessageFormatWriteSet) messageSetToWrite).writeAsyncTo(cloudWriter).thenApply(unused -> null);
    } catch (StoreException e) {
      return FutureUtils.completedExceptionally(e);
    }
  }
Puts a set of messages into the store asynchronously. When the lifeVersion is {@link MessageInfo#LIFE_VERSION_FROM_FRONTEND},
this method is invoked by the responding to the frontend request. Otherwise, it's invoked in the replication thread.
@param messageSetToWrite The message set to write to the store. Only the StoreKey, OperationTime, ExpirationTime,
                         LifeVersion should be used in this method.
@return a {@link CompletableFuture} that will eventually complete successfully when the messages are written to
        store or will contain the {@link StoreException} if an error occurred
ambry-cloud/src/main/java/com/github/ambry/cloud/CloudBlobStore.java