He-Pin opened a new issue, #3695:
URL: https://github.com/apache/pekko/issues/3695
## Summary
When a CI job hangs, nothing captures a thread dump and most jobs have no
`timeout-minutes`, so the run eventually dies on the runner's own global limit
and the log contains no clue about what was blocked. Given that the test suite
has several code paths that can block indefinitely, this turns an investigable
hang into an unexplained red build.
## Current state
There are 40 workflow files under `.github/workflows/`. `timeout-minutes`
appears exactly once in all of them:
```
.github/workflows/nightly-multi-jvm-tests.yml:32: timeout-minutes: 240
```
`build-test-prValidation.yml` — the workflow that runs on every PR — sets
none.
No workflow captures diagnostics on a hang. Searching for the usual
mechanisms returns nothing:
```
rg -in 'jstack|jcmd|thread.?dump|blame-hang' .github/workflows/
(no matches)
```
## Why hangs are plausible here
The testkit contains at least three paths that can block without a deadline:
**`TestKit.within` does not interrupt its block.**
`testkit/src/main/scala/org/apache/pekko/testkit/TestKit.scala:415,426-436`:
```scala
val _max = max.dilated
...
val ret =
try f
finally end = prev_end
val diff = now - start
assert(min <= diff, s"block took ${format(min.unit, diff)}, should at
least have been $min")
if (!lastWasNoMsg) {
assert(diff <= max_diff, s"block took ${format(_max.unit, diff)},
exceeding ${format(_max.unit, max_diff)}")
}
```
The budget is only checked *after* `f` returns. A block that never returns
hangs the test forever; `within` provides no watchdog.
**`Player.enter` awaits with `Duration.Inf`.**
`multi-node-testkit/src/main/scala/org/apache/pekko/remote/testconductor/Player.scala:124`:
```scala
Await.result(client ? ToServer(EnterBarrier(b,
Option(barrierTimeout))), Duration.Inf)
```
**`Controller` blocks on a Netty future inside a message handler.**
`multi-node-testkit/src/main/scala/org/apache/pekko/remote/testconductor/Conductor.scala:531-532`:
```scala
case GetSockAddr =>
sender() ! connection.channelFuture.sync().channel().localAddress()
```
This one is filed separately, but it is an example of the general shape: a
blocked actor thread that also hosts the barrier coordinator.
## Proposal
Two changes, both confined to `.github/`:
1. **Capture thread dumps when a test step exceeds its budget.** Run the sbt
invocation under a watchdog that, on expiry, executes `jcmd <pid> Thread.print`
(or `jstack -l <pid>`) for every JVM in the process tree and uploads the output
as an artifact *before* killing the step. sbt forks test JVMs, so the watchdog
needs to find the children rather than only the sbt launcher.
2. **Add `timeout-minutes` to the PR validation jobs**, set well below the
runner default and above the observed p99 for each job, so a hang fails fast
with the dump attached instead of consuming the full runner limit.
A dump on hang is worth more than the timeout itself: the timeout only tells
you the build is red, while the dump usually names the blocked thread and the
lock or `Await` it is stuck in — which for a distributed-systems test suite is
very often the actual bug rather than a CI annoyance.
## Scope
CI configuration only. No production or test-source change.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]