diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java index 1bd7eecaeff2..0877557e35bd 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java @@ -375,6 +375,22 @@ static AsyncRequestBody fromRemainingByteBuffersUnsafe(ByteBuffer... byteBuffers *

An {@link ExecutorService} is required in order to perform the blocking data reads, to prevent blocking the * non-blocking event loop threads owned by the SDK. * + *

Executor Guidance: The provided executor is used to run a blocking task that reads from the input stream and + * pushes data to the HTTP channel. If the executor has fewer threads than the number of concurrent requests using it, + * tasks are serialized — one slow or failing request may block other requests from writing data in a timely manner, + * leading to idle HTTP connections that the server may close before data can be written. This may result in an + * unrecoverable write timeout loop where every retry also fails. + * + *

To avoid this: + *

+ * + *

It is also recommended to configure an API call timeout via + * {@code ClientOverrideConfiguration.builder().apiCallTimeout()} to bound the total time spent on retries. + * * @param inputStream The input stream containing the data to be sent * @param contentLength The content length. If a content length smaller than the actual size of the object is set, the client * will truncate the stream to the specified content length and only send exactly the number of bytes @@ -390,7 +406,9 @@ static AsyncRequestBody fromInputStream(InputStream inputStream, Long contentLen /** * Creates an {@link AsyncRequestBody} from an {@link InputStream} with the provided - * {@link AsyncRequestBodySplitConfiguration}. + * {@link AsyncRequestBodyFromInputStreamConfiguration}. + * + *

See {@link #fromInputStream(InputStream, Long, ExecutorService)} for guidance on executor. */ static AsyncRequestBody fromInputStream(AsyncRequestBodyFromInputStreamConfiguration configuration) { Validate.notNull(configuration, "configuration"); diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBodyFromInputStreamConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBodyFromInputStreamConfiguration.java index c2d84af45632..ec8272458fb7 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBodyFromInputStreamConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBodyFromInputStreamConfiguration.java @@ -137,6 +137,10 @@ public interface Builder extends CopyableBuilderIt is recommended to have a dedicated executor for SDK input stream requests and have as many threads as the + * number of concurrent requests sharing it. Using an undersized or shared executor may lead to unrecoverable failures. + * See {@link AsyncRequestBody#fromInputStream(InputStream, Long, ExecutorService)} for details. + * * @param executor the executor * @return This object for method chaining. */