Conversation
|
On CI Failures: The CI build failures are unrelated to the code changes. Cargo 1.81 cannot resolve the current crate ecosystem because new crate versions now require edition2024. Seems like infrastructure rot. |
You can update rust-toolchain.toml to 1.95 to fix this. |
I had a PR for this but its not as simple as updating the toolchain. Need to fix the new clippy issues that are introduced in new versions: I can cherry pick or rebase this PR after the above one is merged |
|
LGTM. At the time of implementation, it was simply designed to behave exactly like the Go version, even though the Go version might be incorrect... |
2dadc6b to
c908838
Compare
|
@tzneal @dims Added a regression test in the second commit. |
2afc15c to
afa1063
Compare
Tim-Zhang
left a comment
There was a problem hiding this comment.
The ordering bug is real, but this implementation introduces connection-wide head-of-line blocking.
Each stream uses a bounded channel with capacity 100. If one stream is not consumed, resp_tx.send(...).await blocks Connection::run, preventing it from reading responses for every other stream on the same connection.
I reproduced this by leaving a 200-frame stream unread and issuing an unrelated unary RPC: it times out with this patch but succeeds with the previous implementation.
Increasing the channel capacity would only delay the problem. Please preserve ordering without blocking the connection reader, for example with a per-stream ordered mailbox/dispatcher. The reader should enqueue frames without awaiting the consumer-facing bounded channel; if strict memory bounds are required, apply a per-stream quota and fail only the overflowing stream.
There are also two test issues:
let _ = h.awaitignores task panics and can produce false positives.async-data-orderis not executed bytests/run-examples.rs.
45623ed to
86d2e96
Compare
|
@Tim-Zhang @wllenyj I've incorporated your feedback and updated my implementation (PR description should have the details). I will appreciate if you can do another round of review! |
3f4ffd7 to
ad82d2a
Compare
Spawning a task per incoming client frame allowed DATA frames to reorder and race a following REMOTE_CLOSED frame, silently dropping payloads. Processing frames through the bounded response channel in the connection reader fixed ordering but let one unread stream block every RPC on the connection. Give every client stream ID, including unary RPCs, an unbounded ordered response queue. The connection reader transforms and enqueues results directly in wire order without waiting for the consumer. Each RPC reads that queue through the existing receive path, so a slow consumer affects only its own stream. Keep the server's existing bounded handler channel. Add regressions for close ordering, data ordering, and an unread stream sharing a connection with a unary RPC. Propagate spawned-task panics and execute the regression examples from the integration suite. Fixes containerd#311 Signed-off-by: Shiv Bhosale <shvbsle@amazon.com>
Fixes #311
Problem
The async client previously spawned one Tokio task per incoming frame. Tasks for the same stream could execute out of order, allowing a terminal
REMOTE_CLOSEDframe to remove the stream before an earlier DATA frame was delivered. This caused silent data loss and frame reordering. Processing frames directly through the existing bounded response channel preserved ordering but introduced connection-wide head-of-line blocking.Solution
Give every client RPC an ordered per-stream mailbox:
ClientReaderapplies inbound transforms and enqueues frames into the corresponding unbounded mailbox in wire order.The unbounded ingress queue is intentional. TTRPC has no per-stream receive-window mechanism, so a hard bound would require either blocking the whole connection or failing an individual stream at an arbitrary limit. This replaces the previous unbounded task-per-frame behavior with one queue and one dispatcher task per active RPC.
Regression tests
Added three self-contained regression examples:
async-stream-close-ordersends DATA immediately followed byREMOTE_CLOSEDacross 2,000 calls and verifies the payload always arrives before EOF.async-data-ordersends 50 sequential frames across 400 concurrent streams and verifies every stream receives them in order.async-slow-consumerleaves a 500-frame stream unread, issues an unrelated unary RPC on the same connection, and verifies that the unary RPC completes and all stream frames remain available.All three examples are executed by
tests/run-examples.rs, and spawned-task panics are propagated. Unit tests also verify mailbox ordering beyond the bounded channel capacity and delivery of terminal errors after buffered frames.Compatibility
The public API and wire format are unchanged. Applications continue using the existing unary and streaming receive APIs without any additional handling.