perf(durable-streams-rust): serve caught-up long-polls from the epoll reactor#4667
Open
balegas wants to merge 1 commit into
Open
perf(durable-streams-rust): serve caught-up long-polls from the epoll reactor#4667balegas wants to merge 1 commit into
balegas wants to merge 1 commit into
Conversation
… reactor Generalize the SSE reactor (renamed sse_reactor -> reactor) to serve two reader modes from one slab/epoll/flush/wake core. A caught-up long-poll on a reactor- eligible stream now hands its socket to the reactor instead of parking the whole connection-task future for the wait; the reactor writes the single response on data/close/timeout and, for a keep-alive client, hands the socket back to the async connection loop so the client's next request is read and dispatched there (re-registering with the reactor if it is another caught-up poll). Reusing the existing async keep-alive path keeps the reactor free of a second HTTP request reader. Linux only; non-Linux and cold/forked/tiered streams keep the inline wait. SSE behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9647ac4 to
ebd8016
Compare
Contributor
Author
Simplified: dropped the in-reactor keep-alive request reader (design 1)Per review, removed the ~200-line in-reactor HTTP request reader. After writing a keep-alive long-poll response the reactor now hands the socket back to the async Net effect:
Validation (Linux):
|
balegas
added a commit
that referenced
this pull request
Jul 3, 2026
…neralized reactor Merges origin/long-poll-reactor (serve caught-up long-polls from the epoll reactor, sse_reactor -> reactor rename) and adapts it to this branch: - Port the per-stream wake-coalescing latch (StreamSubs::wake_pending) into the generalized reactor: wake_stream's queued-wake skip, the drain loop's clear-before-produce (now covering both Kind::Sse and Kind::LongPoll — longpoll_produce reads durable_tail after the clear, preserving the clear-then-read invariant), and the register-resets-latch rule. Latch and registration stay serialized under the reactor_subs mutex, so a long-poll registering against an in-flight coalesced wake cannot be stranded. - Resolve handlers.rs against the removed splice append path (the incoming branch predated its deletion) and drop the now-unused AtomicBool import in engine_raw.rs. - Fold the long-poll changeset into the branch's single consolidated changeset; document the reactor-served long-poll wait and wake coalescing in ARCHITECTURE.md's read-path section. Validation: cargo test --release 100 passed / 0 failed (2 ignored, Linux-only); cargo clippy --all-targets -D warnings clean for x86_64-unknown-linux-gnu (compiles the reactor + its unit tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Generalizes the Linux epoll reactor (renamed
sse_reactor→reactor) to serve two reader modes from one shared slab/epoll/flush/wake core. A caught-up long-poll on a reactor-eligible stream now hands its socket to the reactor instead of parking the whole connection-task future for the up-to-30s wait — the same per-subscriber memory win the SSE reactor already gives live tails.Kind::{Sse, LongPoll}splits only what differs (what to produce on a wake, what to do when finished); slab, epoll arm/flush/backpressure, cross-thread wake and lifetime sweep are shared.long_poll_data_resp,long_poll_close,long_poll_timeout) so the inline path and the reactor emit byte-identical responses.Socket-closure (
Connection: close) checkThe base SSE PR's final fix marks SSE responses
Connection: closebecause SSE unilaterally closes the socket (close-instead-of-FIN → RST → discarded in-flight response on a pooling client). I verified the long-poll reactor does not have this class of bug, by construction:Connectionheader is tied to the samekeep_aliveflag that decides close-vs-handback (write_heademitsconnection: closeiff!keep_alive;finishkeeps the socket iffkeep_alive). The disposition can never disagree with what was advertised.close()of a keep-alive socket happens with a drained recv buffer (the in-place reader reads toEAGAINbefore deciding; parked closes carry no in-flight body), so it's a clean FIN, never an RST.The reactor's SSE path inherits the
Connection: closefix automatically (its head is built fromhandle_sse's headers).Testing
x86_64-unknown-linux-gnu) and native builds pass.connection: close, 204 timeout → keep-alive).🤖 Generated with Claude Code