From aeeef6927e23758bd1836042a1bf1ed08e40878a Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:43:02 -0800 Subject: [PATCH 1/7] Add keepalive to responseInputStream timeout scheduler executor to make sure scheduler thread doenst leak --- .../amazon/awssdk/core/ResponseInputStream.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java index 8a7d9fea9b89..4c0ecd9d43e0 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java @@ -21,6 +21,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; @@ -140,12 +141,17 @@ private void scheduleTimeoutTask(Duration timeout) { } private static final class TimeoutScheduler { - static final ScheduledExecutorService INSTANCE = - Executors.newScheduledThreadPool(1, r -> { + static final ScheduledExecutorService INSTANCE; + static { + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "response-input-stream-timeout-scheduler"); t.setDaemon(true); return t; }); + executor.setKeepAliveTime(DEFAULT_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + executor.allowCoreThreadTimeOut(true); + INSTANCE = executor; + } } /** From a0d4a111fe9f682ade41528a5594a5a2f98aed30 Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:42:31 -0800 Subject: [PATCH 2/7] Fix checkstyle --- .../java/software/amazon/awssdk/core/ResponseInputStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java index 4c0ecd9d43e0..7c5849ca299f 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.InputStream; import java.time.Duration; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -142,6 +141,7 @@ private void scheduleTimeoutTask(Duration timeout) { private static final class TimeoutScheduler { static final ScheduledExecutorService INSTANCE; + static { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "response-input-stream-timeout-scheduler"); From 9a1ce25aa110cdd24f624e2c4d5893205e67f0cd Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:44:36 -0800 Subject: [PATCH 3/7] Add changelog --- .changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json new file mode 100644 index 000000000000..7588be9339c0 --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Fix resource leak in ResponseInputStream" +} From 463c47bbf3f19c8913a5f817163de5f32b5aad36 Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:44:30 -0800 Subject: [PATCH 4/7] Separate timeout value, and extend fix to ResponsePublisher --- .../amazon/awssdk/core/ResponseInputStream.java | 3 ++- .../amazon/awssdk/core/async/ResponsePublisher.java | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java index 7c5849ca299f..8f87186d0edd 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/ResponseInputStream.java @@ -61,6 +61,7 @@ public final class ResponseInputStream extends SdkFilterInputStream i private static final Logger log = Logger.loggerFor(ResponseInputStream.class); private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(60); + private static final long THREAD_IDLE_TIMEOUT_SECONDS = 60; private final ResponseT response; private final Abortable abortable; private ScheduledFuture timeoutTask; @@ -148,7 +149,7 @@ private static final class TimeoutScheduler { t.setDaemon(true); return t; }); - executor.setKeepAliveTime(DEFAULT_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + executor.setKeepAliveTime(THREAD_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS); executor.allowCoreThreadTimeOut(true); INSTANCE = executor; } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/ResponsePublisher.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/ResponsePublisher.java index bb44b4568f16..d57dd399903e 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/ResponsePublisher.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/ResponsePublisher.java @@ -18,9 +18,9 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.util.Objects; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -50,6 +50,7 @@ public final class ResponsePublisher implements S private static final Logger log = Logger.loggerFor(ResponsePublisher.class); private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(60); + private static final long THREAD_IDLE_TIMEOUT_SECONDS = 60; private final ResponseT response; private final SdkPublisher publisher; private ScheduledFuture timeoutTask; @@ -101,12 +102,18 @@ private void scheduleTimeoutTask(Duration timeout) { } private static final class TimeoutScheduler { - static final ScheduledExecutorService INSTANCE = - Executors.newScheduledThreadPool(1, r -> { + static final ScheduledExecutorService INSTANCE; + + static { + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "response-publisher-timeout-scheduler"); t.setDaemon(true); return t; }); + executor.setKeepAliveTime(THREAD_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + executor.allowCoreThreadTimeOut(true); + INSTANCE = executor; + } } private static class CancellingSubscriber implements Subscriber { From 49be9542b1dbcc2eb43548330ef0c5a4d7a74770 Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:47:05 -0800 Subject: [PATCH 5/7] Fix changelog --- .changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json index 7588be9339c0..566f442d1cd6 100644 --- a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json @@ -2,5 +2,5 @@ "type": "bugfix", "category": "AWS SDK for Java v2", "contributor": "", - "description": "Fix resource leak in ResponseInputStream" + "description": "Fix resource leak in ResponseInputStream and ResponsePublisher" } From 4310e4795377b8c626ed0b25370476e207074ceb Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:02:41 -0800 Subject: [PATCH 6/7] Update changelog --- .changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json index 566f442d1cd6..c2cb57854d07 100644 --- a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json @@ -2,5 +2,5 @@ "type": "bugfix", "category": "AWS SDK for Java v2", "contributor": "", - "description": "Fix resource leak in ResponseInputStream and ResponsePublisher" + "description": "Fixed a thread leak in ResponseInputStream and ResponsePublisher where the internal timeout scheduler thread persisted for the lifetime of the JVM, even when no streams were active. The thread now terminates after being idle for 60 seconds.\n" } From 842b12b5075dd182a62ac78f145f5e1b93969760 Mon Sep 17 00:00:00 2001 From: RanVaknin <50976344+RanVaknin@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:03:19 -0800 Subject: [PATCH 7/7] Fix changelog --- .changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json index c2cb57854d07..cc36d1b0808a 100644 --- a/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-7d9ef28.json @@ -2,5 +2,5 @@ "type": "bugfix", "category": "AWS SDK for Java v2", "contributor": "", - "description": "Fixed a thread leak in ResponseInputStream and ResponsePublisher where the internal timeout scheduler thread persisted for the lifetime of the JVM, even when no streams were active. The thread now terminates after being idle for 60 seconds.\n" + "description": "Fixed a thread leak in ResponseInputStream and ResponsePublisher where the internal timeout scheduler thread persisted for the lifetime of the JVM, even when no streams were active. The thread now terminates after being idle for 60 seconds." }