Skip to content

TEZ-4725: Fix flaky tests in TestAMRecoveryAggregationBroadcast#520

Open
maheshrajus wants to merge 1 commit into
apache:masterfrom
maheshrajus:TEZ-4725_flaky
Open

TEZ-4725: Fix flaky tests in TestAMRecoveryAggregationBroadcast#520
maheshrajus wants to merge 1 commit into
apache:masterfrom
maheshrajus:TEZ-4725_flaky

Conversation

@maheshrajus

Copy link
Copy Markdown
Contributor

Root causes and fixes:

  1. TestAMRecoveryAggregationBroadcast.testMapJoinTemporalFailure (race condition)
    Replace fixed Thread.sleep(10s) before AM kill with a deterministic
    waitForVertexSucceeded() helper that polls DAGClient.getVertexStatus()
    every 500ms (up to 60s) until the target vertices reach SUCCEEDED state.
    Each test now waits for only the vertices it logically depends on before
    killing the AM, ensuring the recovery log assertions always see the
    expected counts.
    Make OUT_PATH unique per test run (random suffix) to eliminate cross-test.

  2. DAGClientRPCImpl / TezClientUtils: port out of range:-1 (YARN-808 gap)
    YARN sets rpcPort=-1 when an AM container is allocated (state=RUNNING)
    but the AM has not yet bound its RPC listener. The existing guard only
    checked rpcPort==0 (protobuf default), so rpcPort==-1 reached
    NetUtils.createSocketAddrForHost(), which threw
    IllegalArgumentException: port out of range:-1.
    Fix DAGClientRPCImpl.createAMProxyIfNeeded(): rpcPort == 0 → rpcPort <= 0.
    Fix TezClientUtils.getAMProxy(FrameworkClient,...): add the same
    rpcPort <= 0 guard

@tez-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 4m 11s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 detsecrets 0m 1s detect-secrets was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
+1 💚 test4tests 0m 0s The patch appears to include 3 new or modified test files.
_ master Compile Tests _
+0 🆗 mvndep 0m 35s Maven dependency ordering for branch
+1 💚 mvninstall 4m 33s master passed
+1 💚 compile 3m 59s master passed
+1 💚 checkstyle 1m 4s master passed
+1 💚 javadoc 1m 11s master passed
+0 🆗 spotbugs 1m 23s tez-api in master has 92 extant spotbugs warnings.
+0 🆗 spotbugs 0m 53s tez-tests in master has 6 extant spotbugs warnings.
_ Patch Compile Tests _
+0 🆗 mvndep 0m 9s Maven dependency ordering for patch
+1 💚 mvninstall 3m 51s the patch passed
+1 💚 codespell 1m 37s No new issues.
+1 💚 compile 4m 5s the patch passed
+1 💚 javac 4m 5s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
-0 ⚠️ checkstyle 0m 30s /results-checkstyle-tez-api.txt tez-api: The patch generated 2 new + 62 unchanged - 0 fixed = 64 total (was 62)
+1 💚 javadoc 1m 10s the patch passed
+1 💚 spotbugs 2m 33s the patch passed
_ Other Tests _
+1 💚 unit 75m 58s root in the patch passed.
+1 💚 asflicense 1m 0s The patch does not generate ASF License warnings.
111m 9s
Subsystem Report/Notes
Docker ClientAPI=1.55 ServerAPI=1.55 base: https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-520/1/artifact/out/Dockerfile
Optional Tests dupname compile unit asflicense javac javadoc spotbugs checkstyle codespell detsecrets
uname Linux c15aef73b5a9 5.15.0-181-generic #191-Ubuntu SMP Fri May 22 19:09:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality tez-personality.sh
git revision master / 4bccf3a
Default Java Eclipse Adoptium-21.0.11+10-LTS
Test Results https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-520/1/testReport/
Max. process+thread count 1525 (vs. ulimit of 5500)
modules C: tez-api tez-tests U: .
Console output https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-520/1/console
versions git=2.43.0 maven=3.9.15 spotbugs=4.9.3 codespell=2.4.1
Powered by Apache Yetus 0.15.1 https://yetus.apache.org

This message was automatically generated.

Comment on lines +252 to +253
TezCounters counters = runDAGAndVerify(dag, true,
Collections.singletonList(TABLE_SCAN));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line break is not needed here

private void waitForVertexSucceeded(DAGClient dagClient, String vertexName,
long timeoutMs) throws Exception {
long deadline = System.currentTimeMillis() + timeoutMs;
while (System.currentTimeMillis() < deadline) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to use a monothonic clock instead of System.currentTimeMillis()

Comment on lines +275 to +276
TezCounters counters = runDAGAndVerify(dag, true,
Arrays.asList(TABLE_SCAN, AGGREGATION));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line break is not needed here

Comment on lines +363 to +364
waitForVertexSucceeded(dagClient, vertexName,
TimeUnit.SECONDS.toMillis(60));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line break is not needed here

Comment on lines +395 to +404
if (state == VertexStatus.State.SUCCEEDED) {
return;
}
if (state == VertexStatus.State.FAILED
|| state == VertexStatus.State.KILLED
|| state == VertexStatus.State.ERROR) {
throw new AssertionError("Vertex " + vertexName
+ " reached terminal non-success state: " + state);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about a switch case here, something like

switch (lastState) {
  case SUCCEEDED -> { return; }
  case FAILED, KILLED, ERROR -> throw new AssertionError("Vertex " + vertexName
      + " reached terminal non-success state: " + lastState);
...
}

// and retry rather than crash.
String amHost = appReport.getHost();
int amRpcPort = appReport.getRpcPort();
if (amHost == null || amHost.equals("N/A") || amRpcPort <= 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this condition can be refactored to a separate method with appReport parameter and be reused across different codepaths

Comment on lines +964 to +974
// YARN-808 gap: when the AM container is first allocated YARN briefly
// reports state=RUNNING before the AM has registered with the RM or bound
// its RPC listener. During that window the ApplicationReport contains
// sentinel values that must not be passed to
// NetUtils.createSocketAddrForHost() (which would throw
// IllegalArgumentException: port out of range) or to RPC.getProxy():
// host == null / "N/A" : RM has not received registerApplicationMaster()
// rpcPort == 0 : protobuf wire default
// rpcPort == -1 : container up but RPC listener not yet bound
// Returning null lets callers (waitForProxy, sendAMHeartbeat) back off
// and retry rather than crash.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this long comment can be put to a new method that contains the refactored condition below

except the "return null lets callers" part, which belongs to this method

Comment on lines +283 to +287
// Workaround: check the default strings/sentinels set by YARN.
// port == 0 : protobuf wire default, AM has not called
// registerApplicationMaster() yet.
// port == -1 : AM container is allocated (state=RUNNING) but the
// RPC listener has not been bound yet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment can be put to the new method that contains the refactored condition below

Comment on lines +754 to +761
private DAGClientRPCImplWithFakeReport mockReportClient(TezConfiguration conf,
String host, int rpcPort) throws IOException {
ApplicationReport report = mock(ApplicationReport.class);
when(report.getYarnApplicationState()).thenReturn(YarnApplicationState.RUNNING);
when(report.getHost()).thenReturn(host);
when(report.getRpcPort()).thenReturn(rpcPort);
return new DAGClientRPCImplWithFakeReport(mockAppId, dagIdStr, conf, report);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's strange that mocking and real classes are mixed: if there is a real DAGClientRPCImplWithFakeReport, it could leverage a real ApplicationReport, couldn't it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants