This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new 0e1d7f09c5 [Test][Zeta] Use loopback IP in engine server tests (#11834)
0e1d7f09c5 is described below
commit 0e1d7f09c5e51fc4909281cfaa081cf71b970715
Author: Daniel <[email protected]>
AuthorDate: Tue Sep 1 07:30:25 2026 +0000
[Test][Zeta] Use loopback IP in engine server tests (#11834)
---
.../engine/server/AbstractSeaTunnelServerTest.java | 3 +-
.../server/TestStallThreadDumpExtension.java | 186 +++++++++++++++++++++
.../resourcemanager/FakeResourceManager.java | 2 +-
...akeResourceManagerForExternalExceptionTest.java | 2 +-
...FakeResourceManagerForRequestSlotRetryTest.java | 2 +-
.../resourcemanager/ResourceManagerTest.java | 4 +-
.../server/resourcemanager/WorkerTagTest.java | 2 +-
.../engine/server/rest/OptionRulesApiTest.java | 2 +-
.../RestApiSubmitJobConfigShadeDecryptTest.java | 2 +-
.../RestApiSubmitJobStartWithSavePointTest.java | 2 +-
.../org.junit.jupiter.api.extension.Extension} | 25 +--
.../src/test/resources/batch_fake_to_inmemory.conf | 4 +-
.../src/test/resources/hazelcast-client.yaml | 30 ++--
.../src/test/resources/hazelcast.yaml | 4 +-
.../src/test/resources/junit-platform.properties | 24 +++
.../src/test/resources/log4j2-test.properties | 19 +++
16 files changed, 261 insertions(+), 52 deletions(-)
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/AbstractSeaTunnelServerTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/AbstractSeaTunnelServerTest.java
index 194e172e65..d95e7278ac 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/AbstractSeaTunnelServerTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/AbstractSeaTunnelServerTest.java
@@ -74,6 +74,7 @@ public abstract class AbstractSeaTunnelServerTest<T extends
AbstractSeaTunnelSer
}
protected String getHazelcastConfig() {
+ // Use the IPv4 loopback address to avoid dual-stack localhost
resolution on Windows CI.
return "hazelcast:\n"
+ " cluster-name: seatunnel\n"
+ " network:\n"
@@ -86,7 +87,7 @@ public abstract class AbstractSeaTunnelServerTest<T extends
AbstractSeaTunnelSer
+ " tcp-ip:\n"
+ " enabled: true\n"
+ " member-list:\n"
- + " - localhost\n"
+ + " - 127.0.0.1\n"
+ " port:\n"
+ " auto-increment: true\n"
+ " port-count: 100\n"
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/TestStallThreadDumpExtension.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/TestStallThreadDumpExtension.java
new file mode 100644
index 0000000000..580b6ba464
--- /dev/null
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/TestStallThreadDumpExtension.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.engine.server;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Logs a full JVM thread dump when a test class's bootstrap stalls, so hangs
like the recurring
+ * Windows-only {@code IllegalStateException: Node failed to start!} become
diagnosable from CI
+ * logs.
+ *
+ * <p>That failure surfaces after ~311 seconds (Hazelcast's 300-second default
{@code
+ * hazelcast.max.join.seconds} plus shutdown overhead) with no engine log line
between the config
+ * load and the exception, so the blocked frame never reaches CI output and
each occurrence has been
+ * untraceable. The stall always happens inside {@code @BeforeAll} while {@code
+ * SeaTunnelServerStarter.createHazelcastInstance} boots the local member,
which is why this
+ * extension only monitors the window between the {@code beforeAll} callback
and the first {@code
+ * beforeEach} (or {@code afterAll} for classes whose tests never run):
legitimate long-running test
+ * methods must not trigger spurious dumps.
+ *
+ * <p>The dump threshold is intentionally below Hazelcast's 300-second join
ceiling so the dump is
+ * taken while the startup thread is still parked in the blocked frame, not
after the node has
+ * already given up and torn itself down.
+ *
+ * <p>Registered via {@code
META-INF/services/org.junit.jupiter.api.extension.Extension} with {@code
+ * junit.jupiter.extensions.autodetection.enabled=true} rather than a launcher
{@code
+ * TestExecutionListener}, because the module's test classpath only provides
junit-jupiter-api and
+ * adding junit-platform-launcher would require a pom change.
+ */
+@Slf4j
+public class TestStallThreadDumpExtension
+ implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback {
+
+ /** Bootstrap time budget after which a stall is assumed and a thread dump
is logged. */
+ private static final long STALL_THRESHOLD_MILLIS =
TimeUnit.SECONDS.toMillis(240);
+
+ /** How often the watchdog thread re-checks the tracked bootstrap windows.
*/
+ private static final long SCAN_PERIOD_MILLIS =
TimeUnit.SECONDS.toMillis(30);
+
+ /**
+ * Test classes currently inside their bootstrap window, mapped to the
wall-clock start
+ * timestamp of that window.
+ */
+ private static final Map<String, Long> BOOTSTRAP_START_MILLIS = new
ConcurrentHashMap<>();
+
+ /** Classes already dumped once, so a stalled class does not flood the log
every scan. */
+ private static final Set<String> ALREADY_DUMPED =
ConcurrentHashMap.newKeySet();
+
+ /** Ensures the single watchdog daemon thread is started at most once per
JVM. */
+ private static final AtomicBoolean WATCHDOG_STARTED = new
AtomicBoolean(false);
+
+ @Override
+ public void beforeAll(ExtensionContext context) {
+ // Extension beforeAll callbacks run before the class's own @BeforeAll
methods, so this
+ // timestamp covers the whole server-bootstrap window where the known
stalls occur.
+ BOOTSTRAP_START_MILLIS.put(context.getRequiredTestClass().getName(),
currentTimeMillis());
+ startWatchdogIfNeeded();
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) {
+ // The first test-method setup proves @BeforeAll completed; stop
monitoring this class so
+ // long-running test methods cannot trigger a spurious dump.
+
BOOTSTRAP_START_MILLIS.remove(context.getRequiredTestClass().getName());
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) {
+ // Covers classes whose test methods never start (all skipped, or
bootstrap threw).
+
BOOTSTRAP_START_MILLIS.remove(context.getRequiredTestClass().getName());
+ }
+
+ private static void startWatchdogIfNeeded() {
+ if (!WATCHDOG_STARTED.compareAndSet(false, true)) {
+ return;
+ }
+ Thread watchdog =
+ new Thread(TestStallThreadDumpExtension::scanLoop,
"seatunnel-test-stall-watchdog");
+ // A daemon thread must never keep the surefire JVM alive after the
test run finishes.
+ watchdog.setDaemon(true);
+ watchdog.start();
+ }
+
+ private static void scanLoop() {
+ while (true) {
+ try {
+ Thread.sleep(SCAN_PERIOD_MILLIS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ long now = currentTimeMillis();
+ for (Map.Entry<String, Long> entry :
BOOTSTRAP_START_MILLIS.entrySet()) {
+ long elapsedMillis = now - entry.getValue();
+ if (elapsedMillis >= STALL_THRESHOLD_MILLIS &&
ALREADY_DUMPED.add(entry.getKey())) {
+ log.error(
+ "Test class {} has been inside its bootstrap
window for {} seconds;"
+ + " logging a full thread dump before the
Hazelcast join"
+ + " timeout converts this stall into an
opaque"
+ + " 'Node failed to start!' failure.\n{}",
+ entry.getKey(),
+ TimeUnit.MILLISECONDS.toSeconds(elapsedMillis),
+ fullThreadDump());
+ }
+ }
+ }
+ }
+
+ /**
+ * Renders every live thread with its complete stack.
+ *
+ * <p>{@link ThreadInfo#toString()} silently truncates stacks to 8 frames,
which is useless for
+ * locating a frame parked deep inside Hazelcast bootstrap, so the frames
are formatted
+ * manually.
+ */
+ private static String fullThreadDump() {
+ ThreadInfo[] threads =
ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
+ StringBuilder dump = new StringBuilder(threads.length * 512);
+ for (ThreadInfo thread : threads) {
+ if (thread == null) {
+ continue;
+ }
+ dump.append('"')
+ .append(thread.getThreadName())
+ .append("\" id=")
+ .append(thread.getThreadId())
+ .append(' ')
+ .append(thread.getThreadState());
+ if (thread.getLockName() != null) {
+ dump.append(" on ").append(thread.getLockName());
+ }
+ if (thread.getLockOwnerName() != null) {
+ dump.append(" owned by \"")
+ .append(thread.getLockOwnerName())
+ .append("\" id=")
+ .append(thread.getLockOwnerId());
+ }
+ dump.append('\n');
+ for (StackTraceElement frame : thread.getStackTrace()) {
+ dump.append(" at ").append(frame).append('\n');
+ }
+ for (MonitorInfo monitor : thread.getLockedMonitors()) {
+ dump.append(" locked ")
+ .append(monitor)
+ .append(" at frame depth ")
+ .append(monitor.getLockedStackDepth())
+ .append('\n');
+ }
+ dump.append('\n');
+ }
+ return dump.toString();
+ }
+
+ private static long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+}
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManager.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManager.java
index 7755effb74..3c739ce701 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManager.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManager.java
@@ -51,7 +51,7 @@ public class FakeResourceManager extends
AbstractResourceManager {
}
private void generateWorker(int port) throws UnknownHostException {
- Address address = new Address("localhost", port);
+ Address address = new Address("127.0.0.1", port);
WorkerProfile workerProfile =
new WorkerProfile(
address,
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForExternalExceptionTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForExternalExceptionTest.java
index 259c7fc440..e6691f8069 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForExternalExceptionTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForExternalExceptionTest.java
@@ -47,7 +47,7 @@ public class FakeResourceManagerForExternalExceptionTest
extends AbstractResourc
}
private void generateWorker(int port) throws UnknownHostException {
- Address address = new Address("localhost", port);
+ Address address = new Address("127.0.0.1", port);
WorkerProfile workerProfile =
new WorkerProfile(
address,
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForRequestSlotRetryTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForRequestSlotRetryTest.java
index 40a239b375..7d4f9ca61e 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForRequestSlotRetryTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/FakeResourceManagerForRequestSlotRetryTest.java
@@ -64,7 +64,7 @@ public class FakeResourceManagerForRequestSlotRetryTest
extends AbstractResource
}
private void generateWorker(int port) throws UnknownHostException {
- Address address = new Address("localhost", port);
+ Address address = new Address("127.0.0.1", port);
WorkerProfile workerProfile =
new WorkerProfile(
address,
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/ResourceManagerTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/ResourceManagerTest.java
index 0e76b5114c..a2466077a7 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/ResourceManagerTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/ResourceManagerTest.java
@@ -235,7 +235,7 @@ public class ResourceManagerTest extends
AbstractSeaTunnelServerTest<ResourceMan
List<ResourceProfile> resourceProfiles = new ArrayList<>();
resourceProfiles.add(new ResourceProfile());
ConcurrentMap<Address, WorkerProfile> registerWorker = new
ConcurrentHashMap<>();
- Address address1 = new Address("localhost", 5801);
+ Address address1 = new Address("127.0.0.1", 5801);
WorkerProfile workerProfile1 =
new WorkerProfile(
address1,
@@ -247,7 +247,7 @@ public class ResourceManagerTest extends
AbstractSeaTunnelServerTest<ResourceMan
Collections.emptyMap());
registerWorker.put(address1, workerProfile1);
- Address address2 = new Address("localhost", 5802);
+ Address address2 = new Address("127.0.0.1", 5802);
WorkerProfile workerProfile2 =
new WorkerProfile(
address2,
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/WorkerTagTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/WorkerTagTest.java
index 700e8046f6..6ba193bddc 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/WorkerTagTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/resourcemanager/WorkerTagTest.java
@@ -61,7 +61,7 @@ public class WorkerTagTest extends
AbstractSeaTunnelServerTest<WorkerTagTest> {
+ " tcp-ip:\n"
+ " enabled: true\n"
+ " member-list:\n"
- + " - localhost\n"
+ + " - 127.0.0.1\n"
+ " port:\n"
+ " auto-increment: true\n"
+ " port-count: 100\n"
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/OptionRulesApiTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/OptionRulesApiTest.java
index cc3125dcc1..5737f4722e 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/OptionRulesApiTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/OptionRulesApiTest.java
@@ -161,7 +161,7 @@ class OptionRulesApiTest {
+ " tcp-ip:\n"
+ " enabled: true\n"
+ " member-list:\n"
- + " - localhost\n"
+ + " - 127.0.0.1\n"
+ " port:\n"
+ " auto-increment: true\n"
+ " port-count: 100\n"
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobConfigShadeDecryptTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobConfigShadeDecryptTest.java
index 89cd82ab06..f2d2316b85 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobConfigShadeDecryptTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobConfigShadeDecryptTest.java
@@ -369,7 +369,7 @@ public class RestApiSubmitJobConfigShadeDecryptTest {
+ " tcp-ip:\n"
+ " enabled: true\n"
+ " member-list:\n"
- + " - localhost\n"
+ + " - 127.0.0.1\n"
+ " port:\n"
+ " auto-increment: true\n"
+ " port-count: 100\n"
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobStartWithSavePointTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobStartWithSavePointTest.java
index 2210579a1f..e6ce345c08 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobStartWithSavePointTest.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/RestApiSubmitJobStartWithSavePointTest.java
@@ -521,7 +521,7 @@ public class RestApiSubmitJobStartWithSavePointTest {
+ " tcp-ip:\n"
+ " enabled: true\n"
+ " member-list:\n"
- + " - localhost\n"
+ + " - 127.0.0.1\n"
+ " port:\n"
+ " auto-increment: true\n"
+ " port-count: 100\n"
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
similarity index 60%
copy from
seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
copy to
seatunnel-engine/seatunnel-engine-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
index 9552c382ed..861342d7bd 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -1,4 +1,3 @@
-#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
@@ -6,32 +5,12 @@
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-
-hazelcast-client:
- cluster-name: seatunnel
- network:
- cluster-members:
- - localhost:5801
- - localhost:5802
- - localhost:5803
- - localhost:5804
- - localhost:5805
- - localhost:5806
- - localhost:5807
- - localhost:5808
- - localhost:5809
- - localhost:5810
- - localhost:5811
- - localhost:5812
- - localhost:5813
- - localhost:5814
- - localhost:5815
+org.apache.seatunnel.engine.server.TestStallThreadDumpExtension
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/batch_fake_to_inmemory.conf
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/batch_fake_to_inmemory.conf
index ebe025644a..716f9eb158 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/batch_fake_to_inmemory.conf
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/batch_fake_to_inmemory.conf
@@ -46,7 +46,7 @@ sink {
plugin_input = "fake"
username = "st"
password = "stpassword"
- address = "localhost"
+ address = "127.0.0.1"
port = 1234
}
-}
\ No newline at end of file
+}
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
index 9552c382ed..b256c5f9a5 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast-client.yaml
@@ -20,18 +20,18 @@ hazelcast-client:
network:
cluster-members:
- - localhost:5801
- - localhost:5802
- - localhost:5803
- - localhost:5804
- - localhost:5805
- - localhost:5806
- - localhost:5807
- - localhost:5808
- - localhost:5809
- - localhost:5810
- - localhost:5811
- - localhost:5812
- - localhost:5813
- - localhost:5814
- - localhost:5815
+ - 127.0.0.1:5801
+ - 127.0.0.1:5802
+ - 127.0.0.1:5803
+ - 127.0.0.1:5804
+ - 127.0.0.1:5805
+ - 127.0.0.1:5806
+ - 127.0.0.1:5807
+ - 127.0.0.1:5808
+ - 127.0.0.1:5809
+ - 127.0.0.1:5810
+ - 127.0.0.1:5811
+ - 127.0.0.1:5812
+ - 127.0.0.1:5813
+ - 127.0.0.1:5814
+ - 127.0.0.1:5815
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast.yaml
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast.yaml
index c3596f53b4..af81ee98a8 100644
--- a/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast.yaml
+++ b/seatunnel-engine/seatunnel-engine-server/src/test/resources/hazelcast.yaml
@@ -22,7 +22,7 @@ hazelcast:
tcp-ip:
enabled: true
member-list:
- - localhost
+ - 127.0.0.1
port:
auto-increment: true
port-count: 100
@@ -37,4 +37,4 @@ hazelcast:
path: /tmp/file-store-map
properties:
hazelcast.slow.operation.detector.stacktrace.logging.enabled: true
- hazelcast.slow.operation.detector.logging.enabled: true
\ No newline at end of file
+ hazelcast.slow.operation.detector.logging.enabled: true
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/junit-platform.properties
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/junit-platform.properties
new file mode 100644
index 0000000000..0c3682ddd9
--- /dev/null
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/junit-platform.properties
@@ -0,0 +1,24 @@
+################################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+# Required so TestStallThreadDumpExtension (registered through
+# META-INF/services/org.junit.jupiter.api.extension.Extension) is picked up
without
+# annotating every test class. The extension logs a full thread dump when a
test
+# class's bootstrap stalls, which is the only way to diagnose the recurring
+# Windows-only "Node failed to start!" hang from CI output.
+junit.jupiter.extensions.autodetection.enabled=true
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
index cb58829e4f..bdf2fe133c 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
@@ -64,3 +64,22 @@ appender.routing.route.job.appender.name = job-${ctx:ST-JID}
appender.routing.route.job.appender.fileName =
${file_path}/job-${ctx:ST-JID}.log
appender.routing.route.job.appender.layout.type = PatternLayout
appender.routing.route.job.appender.layout.pattern = %d{yyyy-MM-dd
HH:mm:ss,SSS} %-5p [%-30.30c{1.}] [%t] - %m%n
+
+# The routing appender's system route above references fileAppender, but this
test config
+# historically omitted the definition (it was copied from
config/log4j2.properties without this
+# block), so every server bootstrap logged "ERROR Appender fileAppender cannot
be located. Route
+# ignored" - misleading noise when triaging startup hangs. Mirrors the
production definition.
+appender.file.name = fileAppender
+appender.file.type = RollingFile
+appender.file.fileName = ${file_path}/${file_name}.log
+appender.file.filePattern = ${file_path}/${file_name}.log.%d{yyyy-MM-dd}-%i
+appender.file.append = true
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = [%X{ST-JID}] %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p
[%-30.30c{1.}] [%t] - %m%n
+appender.file.policies.type = Policies
+appender.file.policies.time.type = TimeBasedTriggeringPolicy
+appender.file.policies.time.modulate = true
+appender.file.policies.size.type = SizeBasedTriggeringPolicy
+appender.file.policies.size.size = ${file_split_size}
+appender.file.strategy.type = DefaultRolloverStrategy
+appender.file.strategy.fileIndex = nomax