This is an automated email from the ASF dual-hosted git repository.
MartijnVisser pushed a commit to branch release-2.2
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/release-2.2 by this push:
new aed108ff0dd [FLINK-40070][tests] Fix flaky DynamicParameterITCase
reading rolled JobManager logs
aed108ff0dd is described below
commit aed108ff0ddb163525b32dc3a30ed5b291e73cfe
Author: Martijn Visser <[email protected]>
AuthorDate: Tue Jun 16 11:30:28 2026 -0700
[FLINK-40070][tests] Fix flaky DynamicParameterITCase reading rolled
JobManager logs
The distribution log4j configuration rolls the log file on startup, so the
JobManager startup banner frequently lands in a rolled .log.N file that
FlinkDistribution.searchAllLogs skips; the test then either spins unboundedly
waiting for the banner (multi-hour e2e_4 hang) or parses a half-written
arguments block ("Missing required option: c"). Search rolled logs for the
startup banner and bound the wait so a missing banner fails fast.
Generated-by: Claude Opus 4.8 (1M context)
(cherry picked from commit f9e0756fbfd15c633ae416c2e76f634da46101e8)
---
.../flink/tests/util/flink/FlinkDistribution.java | 17 ++++++-
.../apache/flink/dist/DynamicParameterITCase.java | 54 ++++++++++++++++++----
2 files changed, 60 insertions(+), 11 deletions(-)
diff --git
a/flink-end-to-end-tests/flink-end-to-end-tests-common/src/main/java/org/apache/flink/tests/util/flink/FlinkDistribution.java
b/flink-end-to-end-tests/flink-end-to-end-tests-common/src/main/java/org/apache/flink/tests/util/flink/FlinkDistribution.java
index 404b1ed4555..f4192a53d01 100644
---
a/flink-end-to-end-tests/flink-end-to-end-tests-common/src/main/java/org/apache/flink/tests/util/flink/FlinkDistribution.java
+++
b/flink-end-to-end-tests/flink-end-to-end-tests-common/src/main/java/org/apache/flink/tests/util/flink/FlinkDistribution.java
@@ -435,13 +435,28 @@ public final class FlinkDistribution {
public <T> Stream<T> searchAllLogs(Pattern pattern, Function<Matcher, T>
matchProcessor)
throws IOException {
+ return searchAllLogs(pattern, matchProcessor, false);
+ }
+
+ /**
+ * Searches the distribution's log files for lines matching the given
pattern.
+ *
+ * @param includeRolledLogs whether to also search rolled log files
({@code .log.N}); the
+ * distribution rolls logs on startup, which can move the startup
banner out of the live
+ * {@code .log} file
+ */
+ public <T> Stream<T> searchAllLogs(
+ Pattern pattern, Function<Matcher, T> matchProcessor, boolean
includeRolledLogs)
+ throws IOException {
final List<T> matches = new ArrayList<>(2);
try (Stream<Path> logFilesStream = Files.list(log)) {
final Iterator<Path> logFiles = logFilesStream.iterator();
while (logFiles.hasNext()) {
final Path logFile = logFiles.next();
- if (!logFile.getFileName().toString().endsWith(".log")) {
+ final String fileName = logFile.getFileName().toString();
+ final boolean isRolledLog =
fileName.matches(".*\\.log\\.\\d+");
+ if (!fileName.endsWith(".log") && !(includeRolledLogs &&
isRolledLog)) {
// ignore logs for previous runs that have a number suffix
continue;
}
diff --git
a/flink-end-to-end-tests/flink-end-to-end-tests-common/src/test/java/org/apache/flink/dist/DynamicParameterITCase.java
b/flink-end-to-end-tests/flink-end-to-end-tests-common/src/test/java/org/apache/flink/dist/DynamicParameterITCase.java
index 88ffb1973fe..49a4f081248 100644
---
a/flink-end-to-end-tests/flink-end-to-end-tests-common/src/test/java/org/apache/flink/dist/DynamicParameterITCase.java
+++
b/flink-end-to-end-tests/flink-end-to-end-tests-common/src/test/java/org/apache/flink/dist/DynamicParameterITCase.java
@@ -22,6 +22,7 @@ import org.apache.flink.configuration.ConfigurationUtils;
import org.apache.flink.configuration.GlobalConfiguration;
import org.apache.flink.configuration.JobManagerOptions;
import org.apache.flink.configuration.RestOptions;
+import org.apache.flink.core.testutils.CommonTestUtils;
import org.apache.flink.runtime.entrypoint.ClusterEntrypoint;
import org.apache.flink.runtime.entrypoint.EntrypointClusterConfiguration;
import org.apache.flink.runtime.entrypoint.FlinkParseException;
@@ -36,7 +37,9 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Path;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@@ -52,6 +55,8 @@ class DynamicParameterITCase {
private static final Pattern ENTRYPOINT_CLASSPATH_LOG_PATTERN =
Pattern.compile(".*ClusterEntrypoint +\\[] - +Classpath:.*");
+ private static final Duration LOG_TIMEOUT = Duration.ofMinutes(1);
+
private static final String HOST = "test_host";
private static final int PORT = 8082;
@@ -129,16 +134,14 @@ class DynamicParameterITCase {
dist.callJobManagerScript(args.toArray(new String[0]));
- while (!allProgramArgumentsLogged(dist)) {
- Thread.sleep(500);
- }
-
- try (Stream<String> lines =
- dist.searchAllLogs(ENTRYPOINT_LOG_PATTERN, matcher ->
matcher.group(1))) {
+ // The backgrounded JobManager writes its log asynchronously; wait
until the complete
+ // program arguments block has been flushed, otherwise the parser may
observe a
+ // partially-written block or never observe it at all.
+ final String[] programArguments = waitForProgramArguments(dist);
+ try {
final EntrypointClusterConfiguration entrypointConfig =
- ClusterEntrypoint.parseArguments(
- lines.filter(new
ProgramArgumentsFilter()).toArray(String[]::new));
+ ClusterEntrypoint.parseArguments(programArguments);
final Configuration configuration =
loadConfiguration(entrypointConfig);
@@ -158,6 +161,34 @@ class DynamicParameterITCase {
}
}
+ private static String[] waitForProgramArguments(FlinkDistribution dist)
throws Exception {
+ final List<String[]> captured = new ArrayList<>(1);
+ CommonTestUtils.waitUtil(
+ () -> {
+ // The "Classpath:" line is logged after the program
arguments, so its presence
+ // means the complete arguments block has been flushed.
+ if (!allProgramArgumentsLogged(dist)) {
+ return false;
+ }
+ captured.clear();
+ captured.add(readProgramArguments(dist));
+ return true;
+ },
+ LOG_TIMEOUT,
+ Duration.ofMillis(500),
+ "The JobManager did not log its complete program arguments in
time.");
+ return captured.get(0);
+ }
+
+ private static String[] readProgramArguments(FlinkDistribution dist) {
+ try (Stream<String> lines =
+ dist.searchAllLogs(ENTRYPOINT_LOG_PATTERN, matcher ->
matcher.group(1), true)) {
+ return lines.filter(new
ProgramArgumentsFilter()).toArray(String[]::new);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private static Configuration loadConfiguration(
EntrypointClusterConfiguration entrypointClusterConfiguration) {
final Configuration dynamicProperties =
@@ -170,11 +201,14 @@ class DynamicParameterITCase {
return configuration;
}
- private static boolean allProgramArgumentsLogged(FlinkDistribution dist)
throws IOException {
+ private static boolean allProgramArgumentsLogged(FlinkDistribution dist) {
// the classpath is logged after the program arguments
try (Stream<String> lines =
- dist.searchAllLogs(ENTRYPOINT_CLASSPATH_LOG_PATTERN, matcher
-> matcher.group(0))) {
+ dist.searchAllLogs(
+ ENTRYPOINT_CLASSPATH_LOG_PATTERN, matcher ->
matcher.group(0), true)) {
return lines.iterator().hasNext();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
}