@Override
    public CompletableFuture<List<ObjectInfo>> list(String prefix) {
        TimerUtil timerUtil = new TimerUtil();
        CompletableFuture<List<ObjectInfo>> cf = doList(prefix);
        cf.thenAccept(keyList -> {
            S3OperationStats.getInstance().listObjectsStats(true).record(timerUtil.elapsedAs(TimeUnit.NANOSECONDS));
            logger.info("List objects finished, count: {}, cost: {}ms", keyList.size(), timerUtil.elapsedAs(TimeUnit.MILLISECONDS));
        }).exceptionally(ex -> {
            S3OperationStats.getInstance().listObjectsStats(false).record(timerUtil.elapsedAs(TimeUnit.NANOSECONDS));
            logger.info("List objects failed, cost: {}, ex: {}", timerUtil.elapsedAs(TimeUnit.NANOSECONDS), ex.getMessage());
            return null;
        });
        return cf;
    }
Upload a part of an object to the specified path.

@param options    options (or context) about the write operation
@param path       the path of the object where the part will be uploaded
@param uploadId   the upload ID of the multipart upload
@param partNumber the part number of the part to be uploaded
@param data       the data to be uploaded, it will be released once the finalCf is done
@param attemptCf  the CompletableFuture to complete when a single attempt is done
@param finalCf    the CompletableFuture to complete when the upload operation is done
s3stream/src/main/java/com/automq/stream/s3/operator/AbstractObjectStorage.java