Skip to content

Commit 1d33741

Browse files
authored
Fix Flink IllegalThreadStateException on Java 8 (#36730)
1 parent bf19899 commit 1d33741

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkPipelineExecutionEnvironment.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
2121

2222
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.concurrent.ConcurrentHashMap;
2325
import org.apache.beam.runners.core.metrics.MetricsPusher;
2426
import org.apache.beam.sdk.Pipeline;
2527
import org.apache.beam.sdk.PipelineResult;
@@ -29,8 +31,10 @@
2931
import org.apache.flink.api.common.JobExecutionResult;
3032
import org.apache.flink.api.common.RuntimeExecutionMode;
3133
import org.apache.flink.api.java.ExecutionEnvironment;
34+
import org.apache.flink.api.java.LocalEnvironment;
3235
import org.apache.flink.core.execution.JobClient;
3336
import org.apache.flink.runtime.jobgraph.JobGraph;
37+
import org.apache.flink.streaming.api.environment.LocalStreamEnvironment;
3438
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
3539
import org.apache.flink.streaming.api.graph.StreamGraph;
3640
import org.slf4j.Logger;
@@ -52,6 +56,8 @@ class FlinkPipelineExecutionEnvironment {
5256
private static final Logger LOG =
5357
LoggerFactory.getLogger(FlinkPipelineExecutionEnvironment.class);
5458

59+
private static final Set<ThreadGroup> protectedThreadGroups = ConcurrentHashMap.newKeySet();
60+
5561
private final FlinkPipelineOptions options;
5662

5763
/**
@@ -143,6 +149,7 @@ public PipelineResult executePipeline() throws Exception {
143149
if (flinkBatchEnv != null) {
144150
if (options.getAttachedMode()) {
145151
JobExecutionResult jobExecutionResult = flinkBatchEnv.execute(jobName);
152+
ensureFlinkCleanupComplete(flinkBatchEnv);
146153
return createAttachedPipelineResult(jobExecutionResult);
147154
} else {
148155
JobClient jobClient = flinkBatchEnv.executeAsync(jobName);
@@ -151,6 +158,7 @@ public PipelineResult executePipeline() throws Exception {
151158
} else if (flinkStreamEnv != null) {
152159
if (options.getAttachedMode()) {
153160
JobExecutionResult jobExecutionResult = flinkStreamEnv.execute(jobName);
161+
ensureFlinkCleanupComplete(flinkStreamEnv);
154162
return createAttachedPipelineResult(jobExecutionResult);
155163
} else {
156164
JobClient jobClient = flinkStreamEnv.executeAsync(jobName);
@@ -161,6 +169,41 @@ public PipelineResult executePipeline() throws Exception {
161169
}
162170
}
163171

172+
/** Prevents ThreadGroup destruction while Flink cleanup threads are still running. */
173+
private void ensureFlinkCleanupComplete(Object executionEnv) {
174+
String javaVersion = System.getProperty("java.version");
175+
if (javaVersion == null || !javaVersion.startsWith("1.8")) {
176+
return;
177+
}
178+
179+
if (!(executionEnv instanceof LocalStreamEnvironment
180+
|| executionEnv instanceof LocalEnvironment)) {
181+
return;
182+
}
183+
184+
ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
185+
if (currentThreadGroup == null) {
186+
return;
187+
}
188+
189+
protectedThreadGroups.add(currentThreadGroup);
190+
191+
Thread cleanupReleaser =
192+
new Thread(
193+
() -> {
194+
try {
195+
Thread.sleep(2000); // 2 seconds should be enough for Flink cleanup
196+
} catch (InterruptedException e) {
197+
Thread.currentThread().interrupt();
198+
} finally {
199+
protectedThreadGroups.remove(currentThreadGroup);
200+
}
201+
},
202+
"FlinkCleanupReleaser");
203+
cleanupReleaser.setDaemon(true);
204+
cleanupReleaser.start();
205+
}
206+
164207
private FlinkDetachedRunnerResult createDetachedPipelineResult(
165208
JobClient jobClient, FlinkPipelineOptions options) {
166209
LOG.info("Pipeline submitted in detached mode");

0 commit comments

Comments
 (0)