Skip to content

fix: cancel open request timeouts when the server connection stage stops - #1284

Open
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:server-cancel-request-timeouts-on-stop
Open

fix: cancel open request timeouts when the server connection stage stops#1284
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:server-cancel-request-timeouts-on-stop

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Motivation

RequestTimeoutSupport schedules a request's timeout through materializer.scheduleOnce and keeps the TimeoutAccess in openTimeouts until the response for that request comes back, which is when the timeout is cancelled.

The stage had no postStop, so a connection that goes away with requests still open — the client disconnecting, the connection being aborted, or the server being torn down — left one scheduled task per open request behind. Those tasks keep the request, the timeout handler and the stage's async callback reachable for as long as the configured request-timeout (20 seconds by default), and firing them achieves nothing: they only invoke a callback on a stage that is already gone.

Modification

Cancel everything still in openTimeouts in postStop, using the same best-effort clear() the regular response path uses.

Result

Connections that end before their requests are answered no longer leave scheduled timeouts behind.

Tests

  • New test in HttpServerWithExplicitSchedulerSpec: RequestTimeoutSupport is driven with probes, a request gets a timeout handler that records being called, and both sides are then completed with the request still unanswered. With the explicitly triggered scheduler the test can then let the timeout elapse and assert the handler never runs. Reverting only the postStop addition makes it fail.
  • sbt "http-core/testOnly org.apache.pekko.http.impl.engine.server.HttpServerSpec org.apache.pekko.http.impl.engine.server.HttpServerWithExplicitSchedulerSpec" — pass (66 tests), including the five existing request-timeout tests.
  • sbt "http-core/mimaReportBinaryIssues" — pass.
  • scalafmt --mode diff-ref=upstream/main --test — pass.

References

None — found while auditing src/main for resource leaks.

Motivation:
`RequestTimeoutSupport` schedules a request's timeout through
`materializer.scheduleOnce` and keeps the `TimeoutAccess` in
`openTimeouts` until the response for that request comes back, which is
when the timeout is cancelled.

The stage had no `postStop`, so a connection that goes away with
requests still open - the client disconnecting, the connection being
aborted, or the server being torn down - left one scheduled task per
open request behind. Those tasks keep the request, the timeout handler
and the stage's async callback reachable for as long as the configured
`request-timeout` (20 seconds by default), and firing them achieves
nothing: they only invoke a callback on a stage that is already gone.

Modification:
Cancel everything still in `openTimeouts` in `postStop`, using the same
best-effort `clear()` the regular response path uses.

Result:
Connections that end before their requests are answered no longer leave
scheduled timeouts behind.

Tests:
- sbt "http-core/testOnly org.apache.pekko.http.impl.engine.server.HttpServerSpec org.apache.pekko.http.impl.engine.server.HttpServerWithExplicitSchedulerSpec" - pass (66 tests). Reverting only the `postStop` addition makes the new test fail, as the timeout still fires after the stage stopped.
- sbt "http-core/mimaReportBinaryIssues" - pass.
- scalafmt --mode diff-ref=upstream/main --test - pass.

References:
None - found while auditing `src/main` for resource leaks
@pjfanning
pjfanning marked this pull request as ready for review September 7, 2026 21:03
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