Skip to content

warp: fewer timer operations around streaming response bodies - #1098

Open
seanparsons wants to merge 1 commit into
yesodweb:masterfrom
seanparsons:perf/streaming-timer-ops
Open

warp: fewer timer operations around streaming response bodies#1098
seanparsons wants to merge 1 commit into
yesodweb:masterfrom
seanparsons:perf/streaming-timer-ops

Conversation

@seanparsons

@seanparsons seanparsons commented Jul 19, 2026

Copy link
Copy Markdown

Part of a series splitting #1090 into independently reviewable PRs. Works standalone (halves per-fragment timer ops); compounds with the time-manager tickle rate-limiting PR, which makes the per-fragment tickle nearly free.

sendFragment wrapped every streamed fragment in T.resume/T.pause: two timer-queue operations per fragment. Streaming now resumes once before the body, tickles per fragment, and pauses once after.

Deliberate semantics change for maintainers to weigh: The timeout is armed while user code computes between fragments, so a responseStream that produces nothing for a full timeout period is killed, previously it could stall forever between fragments, since the timeout was only armed during warp's own sends. Streams writing at least once per timeout period (or sending heartbeats) are unaffected, and the sends themselves are now covered, closing a slowloris-shaped gap. Observable for e.g. SSE without heartbeats. Verified manually that a stalled stream closes at the configured timeout.

sendFragment wrapped every streamed fragment in T.resume before the
send and T.pause after it: two timer-queue operations per fragment,
per request. Streaming now resumes the handle once before the body,
tickles per fragment, and pauses once after.

Semantics change, deliberate: the timeout is armed while user code
computes between fragments, so a responseStream that produces nothing
for a full timeout period is killed (previously it could stall forever
between fragments, since the timeout was only armed during warp's own
sends). Streams that write at least once per timeout period (or send
heartbeats) are unaffected; the sends themselves are now also covered,
closing a slowloris-shaped gap. Verified manually that a stalled
stream is closed at the configured timeout while an idle keep-alive
connection still times out normally.

Standalone this halves the per-fragment timer ops (one tickle instead
of resume+pause); it compounds with the time-manager tickle
rate-limiting change, which makes the per-fragment tickle nearly free.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HBrMGfWRxTpYeEB4UT8Kf

@Vlix Vlix left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the change in behaviour.
We shouldn't act like a stream is a DOS/slow-loris if the user code is taking a long time.

Could this maybe work with a sort of "debounced" resume/pause function?

@kazu-yamamoto

Copy link
Copy Markdown
Contributor

and pauses once after.

@seanparsons I don't understand which code this "pauses" refers to.
Could you tell me its filename and line number?

@seanparsons

Copy link
Copy Markdown
Author

and pauses once after.

@seanparsons I don't understand which code this "pauses" refers to. Could you tell me its filename and line number?

Ah yes, it's referring to this pause call higher up the call chain:

@kazu-yamamoto

Copy link
Copy Markdown
Contributor

Thanks. If we were to accept your changes, I think placing resume and pause close to each other would improve readability.

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.

3 participants