Skip to content

Fix flaky Netty request-timeout metrics tests#5381

Merged
adamw merged 2 commits into
masterfrom
fix/netty-request-timeout-test-flakiness
Jul 7, 2026
Merged

Fix flaky Netty request-timeout metrics tests#5381
adamw merged 2 commits into
masterfrom
fix/netty-request-timeout-test-flakiness

Conversation

@adamw

@adamw adamw commented Jul 7, 2026

Copy link
Copy Markdown
Member

Problem

The test properly update metrics when a request times out (in NettyFutureRequestTimeoutTests, and its sync counterpart in NettySyncRequestTimeoutTests) is flaky on CI, failing intermittently with 1 was not equal to 0 at the activeRequests.get() shouldBe 0 assertion. It failed on three dependency-update PRs in the week of 2026-07-06 alone (runs for #5379, #5376 and #5373).

The root cause is a race in the test, not in production code: the metrics are only decremented when the endpoint's logic completes, which for the Future-based server happens ~1 second after the 503 timeout response is received (the Future running the logic is not interruptible). The test used a fixed Thread.sleep(1100), leaving only a ~100 ms margin before asserting — not enough on a loaded CI machine. The sync test had an even tighter fixed Thread.sleep(100).

Fix

Replace the fixed sleeps with eventually { ... } and a widened patience config (15 s timeout / 150 ms interval) — the same idiom already used for the same reason in ServerMetricsTest. This asserts eventual consistency instead of racing the asynchronous metrics update. No production code is changed.

Verification

Ran locally:

  • sbt "nettyServer/testOnly sttp.tapir.server.netty.NettyFutureServerTest -- -z \"properly update metrics\"" — passed
  • sbt "nettyServerSync3/testOnly sttp.tapir.server.netty.sync.NettySyncServerTest -- -z \"properly update metrics\"" — passed

🤖 Generated with Claude Code

adamw and others added 2 commits July 7, 2026 09:16
The "properly update metrics when a request times out" tests asserted
gauge values after a fixed Thread.sleep, racing the asynchronous metrics
update which only happens when the endpoint's (non-interruptible) logic
completes - ~1s after the 503 timeout response for the Future server.
The ~100ms margin was not enough on loaded CI machines.

Replace the fixed sleeps with `eventually` and a widened patience config
(15s / 150ms), the same idiom used in ServerMetricsTest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamw adamw merged commit fb7c824 into master Jul 7, 2026
22 checks passed
@adamw adamw deleted the fix/netty-request-timeout-test-flakiness branch July 7, 2026 12:45
adamw added a commit that referenced this pull request Jul 7, 2026
## Problem

The "Send and receive SSE" test in `AkkaHttpServerTest` flaked on CI
(seen on PR #5381's CI run, 2026-07-07, ci 2.13 / JVM 11) with only:

```
org.scalatest.exceptions.TestFailedException was thrown. (AkkaHttpServerTest.scala:82)
```

No message, no cause. An identical sibling test exists in
`PekkoHttpServerTest`.

Two things went wrong:

1. **Swallowed exception**: the test ended with `.transform(sse => sse
shouldBe ..., ex => fail(ex))`. `fail(ex)` wraps the real exception as
the *cause* of a message-less `TestFailedException`, and the test
reporter prints neither — hence the bare failure line.
2. **The actual flake**: the sttp client backend was created with
default `ConnectionPoolSettings`, whose
`response-entity-subscription-timeout` is **1s**. The test consumes the
response via `asStreamUnsafe` and only materializes the SSE-parsing
`runFold` in a later future callback. On a loaded CI runner that gap can
exceed 1s, at which point the akka/pekko-http client pool fails the
entity stream ("Response entity was not subscribed after 1 second...").

## Fix

- Build the per-test client backend with
`ConnectionPoolSettings(actorSystem).withResponseEntitySubscriptionTimeout(1.minute)`
so a slow runner can't hit the subscription timeout.
- Replace the `.transform(..., ex => fail(ex))` with a plain `.map(...)`
so if the test ever fails again, the real exception (message + type)
propagates to the report instead of a bare `TestFailedException`.
- Close the per-test client backend after the request completes (it was
previously leaked).

Same changes applied to both the akka and pekko variants.

## Force-repro proof

- A
`-Dakka.http.host-connection-pool.response-entity-subscription-timeout=1ms`
override propagates into the (non-forked) test JVM but does *not*
reproduce locally: on an idle machine the stream is subscribed before
the scheduler-tick-granularity timeout task can fire.
- Simulating CI load instead — inserting `Thread.sleep(1500)` before the
`runFold` materialization on unmodified master (default 1s timeout) —
reproduces the **exact** CI symptom: `TestFailedException was thrown.
(AkkaHttpServerTest.scala:82)`.
- The same 1500ms delay with this fix applied: test **passes**.

## Verification

- Both SSE tests (akka + pekko) run 3x each: all green.
- `akkaHttpServer2_12/Test/compile`, `pekkoHttpServer2_12/Test/compile`,
`pekkoHttpServer3/Test/compile`: all pass.
- Full `AkkaHttpServerTest` suite: 301 tests, 0 failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant