This is an automated email from the ASF dual-hosted git repository.
ruanwenjun pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git
The following commit(s) were added to refs/heads/dev by this push:
new 86c6844014 [Chore][Master] Quietly exit WorkerGroupDispatcher loop on
interrupt (#18240)
86c6844014 is described below
commit 86c6844014c063e74fea59bd52b915fffc06f113
Author: Wenjun Ruan <[email protected]>
AuthorDate: Sun May 10 12:38:31 2026 +0800
[Chore][Master] Quietly exit WorkerGroupDispatcher loop on interrupt
(#18240)
WorkerGroupDispatcher#run consumed TaskDispatchableEventBus#take() which
was annotated with @SneakyThrows, so an InterruptedException raised when
the master shuts down (the dispatch thread is parked on the queue) was
rethrown as a RuntimeException and surfaced with a full stack trace —
alarming "thread died" noise during a perfectly graceful shutdown.
Drop @SneakyThrows from take() so it declares InterruptedException, and
let the dispatch loop catch it: re-set the interrupt flag, log a single
info line, and return so the daemon thread exits cleanly.
Also clamp the dispatch-retry waiting time to >= 1s so a freshly-counted
failure does not immediately re-enqueue the task against the same
unhealthy worker group.
In addition, document how to run dolphinscheduler-master tests in the
module's CLAUDE.md: no Docker required, watch out for stale JaCoCo
classes, surefire forks 4 JVMs in parallel, and the trailing
"kill self fork JVM ... 30 seconds after System.exit(0)" line is a
harmless warning.
---
dolphinscheduler-master/CLAUDE.md | 38 +++++++++++++++++++++-
.../task/dispatcher/TaskDispatchableEventBus.java | 6 ++--
.../task/dispatcher/WorkerGroupDispatcher.java | 15 ++++++---
3 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/dolphinscheduler-master/CLAUDE.md
b/dolphinscheduler-master/CLAUDE.md
index 57b1e6bc0b..0028557dc2 100644
--- a/dolphinscheduler-master/CLAUDE.md
+++ b/dolphinscheduler-master/CLAUDE.md
@@ -52,7 +52,43 @@ Exposed interfaces:
## Tests
-`src/test/java` — unit + integration. Integration tests extend
`AbstractMasterIntegrationTestCase`; they simulate distributed scenarios
including failover.
+`src/test/java` — unit + integration. Integration tests extend
`AbstractMasterIntegrationTestCase` (a `@SpringBootTest`) and live under
`server/master/integration/cases/`; they simulate distributed scenarios
including failover.
+
+### Running the suite
+
+From the repo root:
+
+```bash
+# Whole module (unit + integration). Use `clean` to avoid the JaCoCo
+# "Cannot process instrumented class" failure when classes from a previous
+# build are still on disk.
+./mvnw -pl dolphinscheduler-master -am clean test
+
+# Single test class. -Dsurefire.failIfNoSpecifiedTests=false is required
+# whenever -am pulls in upstream modules: the -Dtest filter applies to
+# every reactor module, and surefire fails the build on any module that
+# has zero matches without this flag.
+./mvnw -pl dolphinscheduler-master -am clean test \
+ -Dtest=WorkerGroupDispatcherTest \
+ -Dsurefire.failIfNoSpecifiedTests=false
+
+# Single method
+./mvnw -pl dolphinscheduler-master -am clean test \
+ -Dtest=WorkerGroupDispatcherTest#dispatch \
+ -Dsurefire.failIfNoSpecifiedTests=false
+```
+
+### Local environment
+
+- **No Docker required.** Integration tests boot Spring Boot against an
in-memory H2 (`spring-it-application.yaml`), with a fake registry —
Testcontainers is not used here. The repo-wide `-Dm1_chip=true` flag only
applies to `dolphinscheduler-api-test` / `dolphinscheduler-e2e`; ignore it when
running this module.
+- **Surefire forks 4 JVMs in parallel** (`forkCount=4`, `reuseForks=true`, see
root `pom.xml`). Tests must therefore be parallel-safe — never share static
mutable state across cases. Per-fork JaCoCo files land at
`target/jacoco-${forkNumber}.exec`.
+- The `Surefire is going to kill self fork JVM. The exit has elapsed 30
seconds after System.exit(0).` line at the end is **a harmless warning** —
`BUILD SUCCESS` on the same run is the real signal. It just means a fork held a
non-daemon thread past `System.exit`; do not chase it unless the build itself
fails.
+
+### When tests fail
+
+1. Re-run with `clean` first — the JaCoCo error above and stale generated
sources both vanish after a clean rebuild.
+2. For integration tests, check `target/surefire-reports/*-output.txt` per
fork — exceptions from the embedded master/H2 are logged there, not on stdout.
+3. New lifecycle events / failover paths must be exercised against
`AbstractMasterIntegrationTestCase` (see the gotcha above) — extend an existing
case under `integration/cases/` rather than writing a bare unit test.
## Related modules
diff --git
a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/TaskDispatchableEventBus.java
b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/TaskDispatchableEventBus.java
index f6fdb119b0..e177cf7ec7 100644
---
a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/TaskDispatchableEventBus.java
+++
b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/TaskDispatchableEventBus.java
@@ -20,8 +20,6 @@ package
org.apache.dolphinscheduler.server.master.engine.task.dispatcher;
import org.apache.dolphinscheduler.eventbus.AbstractDelayEventBus;
import
org.apache.dolphinscheduler.server.master.engine.task.dispatcher.event.TaskDispatchableEvent;
-import lombok.SneakyThrows;
-
public class TaskDispatchableEventBus<V extends TaskDispatchableEvent<T>, T
extends Comparable<T>>
extends
AbstractDelayEventBus<V> {
@@ -30,8 +28,8 @@ public class TaskDispatchableEventBus<V extends
TaskDispatchableEvent<T>, T exte
super.publish(v);
}
- @SneakyThrows
- public V take() {
+ @Override
+ public V take() throws InterruptedException {
return super.take();
}
diff --git
a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/WorkerGroupDispatcher.java
b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/WorkerGroupDispatcher.java
index 853e8d2c4b..cecdbd4bd4 100644
---
a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/WorkerGroupDispatcher.java
+++
b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/engine/task/dispatcher/WorkerGroupDispatcher.java
@@ -87,7 +87,14 @@ public class WorkerGroupDispatcher extends BaseDaemonThread {
@Override
public void run() {
while (runningFlag.get()) {
- TaskDispatchableEvent<ITaskExecution> taskEntry =
workerGroupEventBus.take();
+ final TaskDispatchableEvent<ITaskExecution> taskEntry;
+ try {
+ taskEntry = workerGroupEventBus.take();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.info("{} interrupted, exiting run loop", this.getName());
+ return;
+ }
ITaskExecution taskExecution = taskEntry.getData();
try (
TaskExecutorMDCUtils.MDCAutoClosable ignore =
@@ -123,9 +130,9 @@ public class WorkerGroupDispatcher extends BaseDaemonThread
{
// If dispatch failed, will put the task back to the queue
// The task will be dispatched after waiting time.
- // the waiting time will increase multiple of times, but will not
exceed 60 seconds
- long waitingTimeMillis = Math.min(
-
taskExecution.getTaskExecutionContext().increaseDispatchFailTimes() * 1_000L,
60_000L);
+ // the waiting time grows with the fail count, bounded between 1
and 60 seconds
+ long waitingTimeMillis = Math.max(1_000L, Math.min(
+
taskExecution.getTaskExecutionContext().increaseDispatchFailTimes() * 1_000L,
60_000L));
dispatchTask(taskExecution, waitingTimeMillis);
log.warn("Dispatch Task: {} failed will retry after: {}/ms",
taskInstanceId,
waitingTimeMillis, ex);