You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uniffle.apache.org by zu...@apache.org on 2026/08/17 08:49:23 UTC
(uniffle) branch master updated: [#2780] fix(client): Fix prefetch executor thread leaks (#2781)
This is an automated email from the ASF dual-hosted git repository.
zuston pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/uniffle.git
The following commit(s) were added to refs/heads/master by this push:
new b1ac32ffe [#2780] fix(client): Fix prefetch executor thread leaks (#2781)
b1ac32ffe is described below
commit b1ac32ffee1015ab45277821b4015f21eb5cddca
Author: Zhen Wang <64...@qq.com>
AuthorDate: Mon Aug 17 16:49:17 2026 +0800
[#2780] fix(client): Fix prefetch executor thread leaks (#2781)
### What changes were proposed in this pull request?
This PR fixes the prefetch executor thread leak in client read handlers.
The following changes are included:
- Invoke `super.close()` in `HadoopShuffleReadHandler.close()` to shut down the executor owned by `PrefetchableClientReadHandler`.
- Implement `MultiReplicaClientReadHandler.close()` to close all underlying replica read handlers.
- Initialize `prefetchExecutors` with a named daemon thread factory:
```java
ThreadUtils.getThreadFactory("PrefetchableClientReadHandler")
```
The generated thread names follow the `PrefetchableClientReadHandler-%d` format.
### Why are the changes needed?
When prefetch is enabled, each `PrefetchableClientReadHandler` creates a dedicated executor.
`HadoopShuffleReadHandler` overrides `close()` without invoking the parent implementation. In addition, `MultiReplicaClientReadHandler` does not propagate `close()` to its underlying handlers. Consequently, the prefetch executors may remain active after shuffle reading finishes.
Under sustained workloads, the leaked threads can exhaust the process's native-thread resources and cause the following error:
```text
java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
at java.base/java.lang.Thread.start0(Native Method)
at java.base/java.lang.Thread.start(Thread.java:809)
at java.base/java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:945)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1353)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123)
at org.apache.uniffle.storage.handler.impl.PrefetchableClientReadHandler.readShuffleData(PrefetchableClientReadHandler.java:96)
at org.apache.uniffle.storage.handler.impl.HadoopClientReadHandler.readShuffleData(HadoopClientReadHandler.java:228)
at org.apache.uniffle.storage.handler.impl.ComposedClientReadHandler.readShuffleData(ComposedClientReadHandler.java:122)
at org.apache.uniffle.storage.handler.impl.MultiReplicaClientReadHandler.readShuffleData(MultiReplicaClientReadHandler.java:66)
```
Closing the complete handler hierarchy ensures that the executors are shut down correctly. Using named daemon threads also improves thread identification and prevents prefetch threads from keeping the JVM alive.
### Does this PR introduce any user-facing changes?
No.
### How was this patch tested?
---
.../uniffle/storage/handler/impl/HadoopShuffleReadHandler.java | 2 ++
.../uniffle/storage/handler/impl/MultiReplicaClientReadHandler.java | 5 +++++
.../uniffle/storage/handler/impl/PrefetchableClientReadHandler.java | 5 ++++-
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/HadoopShuffleReadHandler.java b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/HadoopShuffleReadHandler.java
index 09372c32f..c49ecdcf2 100644
--- a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/HadoopShuffleReadHandler.java
+++ b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/HadoopShuffleReadHandler.java
@@ -217,6 +217,8 @@ public class HadoopShuffleReadHandler extends DataSkippableReadHandler {
}
public synchronized void close() {
+ super.close();
+
try {
dataReader.close();
} catch (IOException ioe) {
diff --git a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/MultiReplicaClientReadHandler.java b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/MultiReplicaClientReadHandler.java
index 7b2774f6e..41501615e 100644
--- a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/MultiReplicaClientReadHandler.java
+++ b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/MultiReplicaClientReadHandler.java
@@ -100,6 +100,11 @@ public class MultiReplicaClientReadHandler extends AbstractClientReadHandler {
} while (true);
}
+ @Override
+ public void close() {
+ handlers.forEach(ClientReadHandler::close);
+ }
+
@Override
public void updateConsumedBlockInfo(BufferSegment bs, boolean isSkippedMetrics) {
super.updateConsumedBlockInfo(bs, isSkippedMetrics);
diff --git a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/PrefetchableClientReadHandler.java b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/PrefetchableClientReadHandler.java
index 2082911ac..90aedc18a 100644
--- a/storage/src/main/java/org/apache/uniffle/storage/handler/impl/PrefetchableClientReadHandler.java
+++ b/storage/src/main/java/org/apache/uniffle/storage/handler/impl/PrefetchableClientReadHandler.java
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
import org.apache.uniffle.common.ShuffleDataResult;
import org.apache.uniffle.common.exception.RssException;
+import org.apache.uniffle.common.util.ThreadUtils;
import org.apache.uniffle.storage.handler.ClientReadHandlerMetric;
public abstract class PrefetchableClientReadHandler extends AbstractClientReadHandler {
@@ -58,7 +59,9 @@ public abstract class PrefetchableClientReadHandler extends AbstractClientReadHa
this.prefetchTimeoutSec = option.timeoutSec;
this.prefetchResultQueue = new LinkedBlockingQueue<>(option.capacity);
// todo: support multi threads to prefetch
- this.prefetchExecutors = Executors.newFixedThreadPool(1);
+ this.prefetchExecutors =
+ Executors.newFixedThreadPool(
+ 1, ThreadUtils.getThreadFactory("PrefetchableClientReadHandler"));
this.abnormalFetchTag = new AtomicBoolean(false);
this.finishedTag = new AtomicBoolean(false);
this.queueingNumber = new AtomicInteger(0);