Simulate write-side flow control on request - #27
Merged
Merged
Conversation
A stream transport now charges every byte it writes and is credited when the receiving end hands those bytes to its protocol, so what is on the wire, held by a partition or parked behind a paused reader all weigh on the writer. Crossing the high mark pauses the protocol and falling back to the low one resumes it, both synchronously, the way the standard library's own transports do it. None of it applies until net.set_flow_control() arms it. Libraries set write-buffer limits uninvited, so arming on their call would change runs nobody touched; unarmed, nothing is charged and the reported buffer size stays zero.
The reference network workload never asks for flow control, so its two digests for seeds 0, 1 and 2 are pinned from before the feature existed: a user's recorded seed has to keep replaying. The rest covers the armed side — watermark crossings, a drain that waits for the peer's read, teardown and fault edges, and both deadlock shapes. The searched one answers after a short read window rather than a fixed byte count, so how much of the body the server took before its own response filled its buffer follows the latency draws, and seed 2 is where that leaves both ends waiting on each other.
Flow control moves off the cut list and into the network table, with the model stated plainly: what the buffer holds, that pause and resume are synchronous and add no scheduling event of their own, and that the switch is what keeps library-set limits inert. The divergence is stated rather than glossed. The buffer drains when the peer's application receives the bytes, with no read-ahead, so simulated backpressure is tighter than the real thing — which is the point, and is why the standard library's numbers ship inside an opt-in mode.
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.
Write-side flow control was a stated limitation — drain() never blocked, write buffers were unbounded, the peer could not pause your writes. It is now simulated, on request: loop.net.set_flow_control() arms it, and a stream transport's write buffer then holds every byte the peer's protocol has not received yet — on the wire, held by a partition, or parked behind the peer's pause_reading(). Crossing the high-water mark calls pause_writing() synchronously from write(), falling back to the low mark calls resume_writing() at the moment the peer's protocol takes the bytes, and StreamWriter.drain() genuinely waits. A backpressure deadlock is now something a seed can find: the suite plants one and explore() names the failing seed.
Decisions worth reviewing:
Fault edges covered by tests: partition applies backpressure and heal lifts it, peer reset fails a waiting drain, a crashed peer leaves the writer to its own timeout, a crashing writer resolves its own drain, close-while-draining wakes the waiter on the stdlib branch it names.
Rebased over the loopback-keying fix; the credit lookup addresses the peer end by (conn, host, port), so flow control routes correctly across a self-connection too.
516 tests, the slow replay-stability suite, and strict mypy green; per-step benchmark unmoved (4.43–4.49 µs against the published ~4.4 µs).