[ 
https://issues.apache.org/jira/browse/HADOOP-19964?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18104448#comment-18104448
 ] 

ASF GitHub Bot commented on HADOOP-19964:
-----------------------------------------

joseluisll opened a new pull request, #8682:
URL: https://github.com/apache/hadoop/pull/8682

   ### Description of PR
   
   JIRA: [HADOOP-19964](https://issues.apache.org/jira/browse/HADOOP-19964)
   
   A test that fails on timeout now produces exactly one full JVM thread dump,
   with deadlock analysis, taken while the threads are still hung — in every
   module, with no per-pom wiring. That is the diagnostic you actually want for
   a hang, and today you get either nothing or two copies of it.
   
   Two things had to change to get there. `TimedOutTestsListener`, which prints
   the dump, has been dead code since February 2025 and is rewritten here as a
   JUnit Platform listener. And `GenericTestUtils.waitFor` — at 650 call sites
   the most common timeout in the suite — used to inline its own 16 KB dump into
   every `TimeoutException` message; it now routes through that same listener,
   so the message drops to one line and the dump joins the others on stderr.
   
   Scope of the change: 12 files, +336/-86, of which 54 deleted lines are dead
   pom configuration.
   
   This diagnostic has been silently dead since February 2025. HADOOP-19415
   Part 4 moved Surefire onto the JUnit Platform provider, and
   `TimedOutTestsListener` was a JUnit 4 `RunListener` registered through the
   Surefire `listener` provider property — which that provider ignores. Eight
   poms have been carrying the property ever since, registering nothing, and a
   timed-out test has reported only its exception. The failure mode is 
invisible:
   a missing dump looks exactly like a test that never hung.
   
   **What changed**
   
   * `TimedOutTestsListener` is now a JUnit Platform `TestExecutionListener`,
     registered through
     `META-INF/services/org.junit.platform.launcher.TestExecutionListener` in 
the
     hadoop-common test resources. Because it rides in the hadoop-common
     test-jar, it activates anywhere that artifact is on the test classpath.
     That is a wider net than the old wiring ever cast, even when it worked:
     coverage no longer depends on each module remembering to paste a property.
     It detects JUnit 5 `@Timeout`, the JUnit 4 vintage runner's
     `TestTimedOutException` (by class name, so no vintage dependency), and any
     failure whose message says it timed out.
   * Two controls the 2012 original never had: 
`-Dhadoop.test.timedout.dump=false`
     disables the dump entirely, and `-Dhadoop.test.timedout.dump.limit`
     (default 5) caps the dumps one JVM prints, with a single elision notice at
     the cap.
   * One dump per timeout, in one place. `GenericTestUtils.waitFor` built a full
     thread dump into every `TimeoutException` it threw — some 16 KB per 
failure,
     at 650 call sites across 258 files. It now prints that dump through
     `TimedOutTestsListener.dumpForTimeout` and its message shrinks to one line
     saying where the dump went; the listener matches that marker and stays
     quiet. The two properties above now cover `waitFor` as well, which the
     inlined dump obeyed neither of.
   * The dead `listener` property is gone from all eight poms (hadoop-common,
     hadoop-kms, hadoop-hdfs, hadoop-hdfs-httpfs,
     hadoop-mapreduce-client-nativetask, hadoop-mapreduce-client,
     hadoop-mapreduce-project, hadoop-yarn); no pom in the tree references it
     any more. Where removal left an empty `<configuration/>`, that element is
     dropped but the plugin declaration is kept, so no module's surefire
     activation or version resolution changes.
   
   **Why `waitFor` still takes its own dump** rather than deferring to the
   listener: a `TestExecutionListener` is only notified at `executionFinished`,
   which fires once the test method *and its teardown* have unwound. For a hang,
   the threads you need to see are usually gone by then — a MiniDFSCluster has
   been shut down in `@AfterEach`. Dumping inside `waitFor` keeps the capture at
   the instant the wait expired, exactly where it was before this patch. The
   listener remains the only option for `@Timeout`, which gives no earlier hook.
   Both paths share the enable switch and the per-JVM budget.
   
   **Scope and limits** (verified against Surefire 3.5.3): the listener fires 
for
   timeouts that fail *through JUnit* (`@Timeout` and friends). It cannot cover 
a
   fork killed by Surefire at `forkedProcessTimeoutInSeconds` —
   `ForkClient#tryToTimeout` sends the fork `Shutdown.KILL` regardless of the
   configured shutdown strategy, and the fork executes `Runtime.halt()`, which
   bypasses listeners and shutdown hooks alike. That case is handled by
   HADOOP-19950 (Surefire dumpstream capture and CI upload). The two are
   complementary: this listener writes to `System.err`, so its dump lands in
   `surefire-reports/*-output.txt`, already inside the globs HADOOP-19950
   uploads in CI.
   
   **Note for downstream consumers**: the same ServiceLoader registration that
   removes the per-pom wiring also means projects consuming the hadoop-common
   test artifact (HBase, Ozone, Hive, Tez, …) pick the listener up without
   asking for it, and `waitFor`'s exception message changes shape for them too —
   the dump moves out of the message and onto stderr. Nothing in the Hadoop tree
   asserted on that message, but downstream code that did will need adjusting.
   `-Dhadoop.test.timedout.dump=false` opts out of the dumps entirely. A release
   note is attached to the JIRA.
   
   This patch was developed with AI assistance. Contains content generated by
   Claude Code.
   
   ### How was this patch tested?
   
   * End-to-end through the real ServiceLoader path, under a plain `mvn test`: a
     scratch test (not part of this PR) pairing a hanging `@Timeout(3)` method
     with a `GenericTestUtils.waitFor` timeout. Each produced exactly one dump
     in `surefire-reports/*-output.txt`, labelled `Test: 
testHangOnJunitTimeout()`
     and `Timed out in: GenericTestUtils.waitFor` respectively, and the
     `@Timeout` case was confirmed in both `SAME_THREAD` and `SEPARATE_THREAD`
     modes. The `waitFor` message dropped from ~16 KB to a single line, taking
     its report file from 18,223 to 1,642 bytes; it now reads in full:
     `TimeoutException: Timed out waiting for condition. Thread dump printed to 
stderr.`
     The dump is moved, not discarded — it lands in `-output.txt` with the
     others, so total bytes are about the same. The win is that the failure you
     read first is legible.
   * `TestTimedOutTestsListener` (5 tests): thread-dump content and deadlock
     detection (6-thread monitor + synchronizer deadlock), timeout-failure
     detection, the `hadoop.test.timedout.dump=false` kill switch, the per-JVM
     dump limit and its single elision notice, and both halves of the `waitFor`
     change — that it prints its own dump with the right label and that its
     message is now one line, plus that the off switch reaches it. The `waitFor`
     tests drive the real helper, so they fail if either side changes. All pass,
     as does `TestGenericTestUtils` unchanged (13 tests together):
     `mvn -B test -pl hadoop-common-project/hadoop-common 
-Dtest='TestTimedOutTestsListener,TestGenericTestUtils'`
   * Registration verified to ship: the services file is copied into
     `hadoop-common/target/test-classes/META-INF/services/`, i.e. into the
     test-jar, alongside Hadoop's existing service registrations. The same run
     logs `Using auto detected provider
     org.apache.maven.surefire.junitplatform.JUnitPlatformProvider` — which is
     why the old JUnit 4 `listener` property registered nothing.
   * `mvn test-compile` passes on all eight modules whose poms changed. Because
     the `GenericTestUtils` change ships inside the hadoop-common test-jar,
     `hadoop-registry` — a consumer of that artifact — was also test-compiled
     against it: BUILD SUCCESS, so test-jar consumers are unaffected at compile
     time.
   * Checkstyle: `TimedOutTestsListener.java` and 
`TestTimedOutTestsListener.java`
     are both clean. The test file carried two violations inherited from the 
2012
     original — a package-private field in the `Monitor` helper and a brace-less
     `if` — which this PR fixes in passing, since it rewrites that file anyway.
     `GenericTestUtils.java` has pre-existing violations, none on any line this
     PR touches.
   
   ### For code changes:
   
   - [x] Does the title of this PR start with the corresponding JIRA issue id
         (e.g. 'HADOOP-17799. Your PR title ...')?
   - [ ] Object storage: Have the integration tests been executed and the 
endpoint
         declared according to the connector-specific documentation? *N/A*
   - [ ] If adding new dependencies to the code, are these dependencies licensed
         in a way that is compatible for inclusion under
         [ASF 2.0](http://www.apache.org/legal/resolved.html#category-a)?
         *N/A — no new dependencies*
   - [ ] If applicable, have you updated the `LICENSE`, `LICENSE-binary`,
         `NOTICE-binary` files? *N/A*
   
   ### AI Tooling
   
   If an AI tool was used:
   
   - [x] The PR includes the phrase "Contains content generated by <tool>"
         where <tool> is the name of the AI tool used.
   - [x] My use of AI contributions follows the ASF legal policy
         https://www.apache.org/legal/generative-tooling.html
   
   




> Restore TimedOutTestsListener thread dumps on test timeout
> ----------------------------------------------------------
>
>                 Key: HADOOP-19964
>                 URL: https://issues.apache.org/jira/browse/HADOOP-19964
>             Project: Hadoop Common
>          Issue Type: Test
>            Reporter: Jose Luis López
>            Priority: Critical
>
> Goal: a test that fails on @Timeout prints a full thread dump into its
> surefire report. Today it prints nothing, and a timeout without thread state
> is undiagnosable after the fact.
>  
> This is a regression. TimedOutTestsListener (HADOOP-8755, 2012) did exactly
> this until the JUnit 5 migration (HADOOP-19415 Part4) left it implementing no
> listener interface. The Surefire "listener" property that 8 poms still carry
> registers nothing.
>  
> Fix:
> * Reimplement it as a JUnit Platform TestExecutionListener, auto-registered
> via META-INF/services in the hadoop-common test artifact.
> * Remove the dead "listener" property from the 8 poms.
> * -Dhadoop.test.timedout.dump=false turns it off;
> -Dhadoop.test.timedout.dump.limit (default 5) caps dumps per JVM.
>  
> Covers timeouts that fail through JUnit. Does not cover Surefire's fork kill
> (forkedProcessTimeoutInSeconds), which halts the JVM and bypasses listeners.
> Complements HADOOP-19950, whose CI upload globs already capture the report
> files these dumps land in.
>  
> The listener activates for every consumer of the hadoop-common test artifact,
> including HBase, Ozone, Hive and Tez: needs a release note.
>  
> Test-scope only; no production code is touched.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to