Repository navigation
QoSHandler appears incompatible with handlers that intentionally delay response completion #15540
Replies: 2 comments 1 reply
|
The behavior of Doing otherwise would overload the system, for example in terms of buffers to hold request/response data, allocated data structures (for example, queued scheduler tasks), and so on. What is the problem you are trying to solve with your |
It obviously cannot. It is not a limit of It won't happen in a real deployment, and trying to make a benchmark with mocks work in strange ways that are not happening in real deployments would just give you numbers that mean nothing. When using HTTP/1 with chunks (your reproducer), and HTTP/2 or HTTP/3, delaying the last write means that the client won't see the request to be complete. For HTTP/1, the client won't send the next request on that connection; for HTTP/2-3 it means that no new stream will be opened if the connection is already maxed out wrt the number of streams. I recommend that you give up this idea of "the real-work is done, even if I won't send the last bytes of the response until later": it is just plain wrong because the "real work" is not done: the last write could be blocked by TCP congestion or flow control, so you have not completed the response, so the "real work" is not complete. My 2 cents: keep everything as is: your |
Uh oh!
There was an error while loading. Please reload this page.
Summary
We're seeing
QoSHandlercause request failures/timeouts when placed in front of a handler that deliberately delays the completion of a response (e.g. a throttling/rate-shaping handler that holds back the finalwrite()/Callbackto pace delivery, without blocking a thread — usingScheduler.schedule()).Setup
DelayHandler(our custom implementation) wraps the outgoing Response in a Response.Wrapper that interceptswrite(). The last chunk (and therefore the completion callback) is scheduled to fire after a configurable delay via the connector's Scheduler. No worker thread is blocked while waiting.QoSHandler, this works exactly as expected under concurrent load — every request completes in roughly the configured delay with a 200. But we run the risk of having unbounded queued requests.QoSHandlerwired in front ofDelayHandler(limiting concurrent requests viasetMaxRequestCount/setMaxSuspend), concurrent requests start failing once load increases.Suspected root cause
QoSHandlerappears to use theRequest.addCompletionListener(request, this::resume)as its sole signal that a request has "finished" and a slot can be freed for a queued request. Since our handler intentionally defers that callback until the delayed final write succeeds,QoSHandlertreats each request as occupying a slot for the entire delay duration rather than completing once the "real" processing is done.Question for maintainers
Is there a supported way to compose
QoSHandlerwith handlers that intentionally hold response completion open (for pacing/throttling purposes) without it counting fully against the QoS slot/timeout? Is there a different lifecycle signal we should be using, or is this combination simply not intended to be supported? Any guidance on the right pattern here would be appreciated.Repro project
A minimal, self-contained Maven project reproducing this (including a parameterized JUnit test that runs the same load with and without
QoSHandlerto show the behavioral difference) is attached: QoSHandlerRepro. See itsREADMEfor details on the handler chain, class responsibilities, and how to run/interpret the test.qoshandlerrepro.zip
All reactions