Skip to content

feat: Drain in-flight requests on stop - #133

Open
thced wants to merge 3 commits into
masterfrom
fix/stop-inflight-counter
Open

thced wants to merge 3 commits into
masterfrom
fix/stop-inflight-counter

Conversation

@thced

@thced thced commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

OpenApiServer.stop(delay) could wait out the full delay with no request in flight. In the JDK's HttpServer (ServerImpl, JDK 25), if a client closes an idle keep-alive connection just before stop, the connection is cleaned up but the finished latch is never counted down. stop then sleeps the whole delay. A plain-JDK repro stalls in about 1 of 4 rounds. Consumers see it as slow shutdowns and 5 s stalls in tests.

OpenApiServer now counts in-flight requests itself:

  • InFlightCounter is a filter, first on every context the server creates. It counts each request through its handler and afterResponse runnables, with the decrement in finally.

  • stop(delay):

    1. starts HttpServer.stop(delay) on a virtual thread, so the listener closes at once and new connections are refused as before;
    2. waits on the counter for at most delay;
    3. calls HttpServer.stop(0), which releases the JDK's latch.

    Open keep-alive connections are still served until the JDK sees no exchange in flight, as before.

  • Repeated stop: later and concurrent calls wait for the first one to finish. A failure in the first call reaches them as an IllegalStateException.

  • inFlightRequests() is new public API that exposes the count, so callers can build their own drain.

Notes

  • feat: new public API, and a second stop call now waits for the first.
  • GracefulStopIT (11 tests, about 3 s):
    • the raw-socket loop and the interrupt test fail on the old stop;
    • the keep-alive client loop is a secondary guard;
    • the rest specify the behaviour.
  • The OpenApiServerIT$Filters hang is unrelated to this change. One mvn verify run hung in postFilterShortStringMatchesNoBranchReturns400. Looping the same scenario hangs on master at the same rate (3 in 12,000, against 1 in 12,000 on this branch). The cause: the server binds the wildcard address on a random port, and another local process can already hold that port on 127.0.0.1, so localhost reaches it instead. A fix would be a separate change: tests bind and call 127.0.0.1.
  • Not changed: the virtual-thread executor is still never closed on stop.

thced added 2 commits October 15, 2026 19:22
HttpServer.stop(delay) can wait out the full delay with nothing in
flight: when a client closes an idle keep-alive connection just before
stop, the JDK never counts its finished latch down (JDK 25 ServerImpl).

OpenApiServer now counts in-flight requests with a filter on every
context it creates. stop(delay) closes the listener at once through a
background HttpServer.stop(delay), waits on the counter for at most
the delay, then calls HttpServer.stop(0). Later and concurrent stop
calls wait for the first. inFlightRequests() exposes the count.
@thced
thced marked this pull request as ready for review October 15, 2026 17:59
@sonarqubecloud

Copy link
Copy Markdown

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