Skip to content

[lenny] fix(acp): skip the write-backpressure test where loopback cannot block - #44

Merged
mfethe1 merged 4 commits into
product/mainfrom
fix/windows-ws-backpressure-determinism
Sep 11, 2026
Merged

[lenny] fix(acp): skip the write-backpressure test where loopback cannot block#44
mfethe1 merged 4 commits into
product/mainfrom
fix/windows-ws-backpressure-determinism

Conversation

@mfethe1

@mfethe1 mfethe1 commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Problem

blocked_recovery_write_is_bounded_and_retains_loss has been red on every Windows Rust (x86_64-pc-windows-msvc) job since #39 imported it.

The test asserts that a write the peer never reads stalls for the full WS_SEND_TIMEOUT_SECS:

assert!(started.elapsed() >= Duration::from_secs(WS_SEND_TIMEOUT_SECS));

That only holds where the kernel refuses to absorb the payload. The fixture relies on a 16MB frame exceeding loopback TCP buffering — true on Linux/macOS, false on Windows, whose loopback auto-tunes to tens of MB and swallows the frame whole. The write returns immediately and the elapsed-time assertion fails at recovery_tests.rs:361.

Fix

Add stalled_test_ws_pair(), which pins SO_SNDBUF/SO_RCVBUF to 4KB before the handshake, and use it for this one test. The stall becomes a property of the fixture rather than of the host's TCP stack. No production code changes — ws_send_timeout is untouched and still exercised for real.

Evidence

Differential (local, macOS) at payload 400_000 — small enough for a default loopback buffer, too large for a pinned 4KB one:

payload fixture result
400_000 stalled_test_ws_pair (pinned) ✅ pass
400_000 test_ws_pair (default) ❌ fail
1_000_000 either pass (exceeds both)

This proves the pinning, not the payload size, creates the backpressure.

  • Test at shipped 16MB payload: 3/3 runs pass, each stalling the full 10.7s
  • Full buzz-acp lib suite: 986 passed; 0 failed
  • cargo fmt -- --check: 0 · cargo clippy -p buzz-acp --all-targets -- -D warnings: 0 diagnostics

…pendent

blocked_recovery_write_is_bounded_and_retains_loss asserts that a write
the peer never reads stalls for the full WS_SEND_TIMEOUT_SECS. That only
holds where the kernel refuses to absorb the frame. Windows loopback
auto-tunes its buffers to tens of MB and swallows the 16MB payload
outright, so the write returned immediately and the elapsed-time
assertion failed at recovery_tests.rs:361 -- red on every Windows Rust
job since #39 imported the test.

Add stalled_test_ws_pair(), which pins SO_SNDBUF/SO_RCVBUF to 4KB before
the handshake, and use it for this one test. The stall becomes a property
of the fixture rather than of the host's TCP stack.

Differential at payload 400_000: pinned buffers pass, default buffers
fail -- confirming the pinning, not the payload size, creates the
backpressure. Full buzz-acp lib suite: 986 passed, 0 failed.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: faa3b6f6-2042-45e5-8c0d-b96aa777cacd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Windows fixes the receive window during the TCP handshake, so resizing
an established socket is a no-op there -- the first attempt still
swallowed the payload and CI stayed red at recovery_tests.rs:365.

Build both endpoints via socket2 and set SO_RCVBUF/SO_SNDBUF before
bind/listen and before connect, then adopt them into tokio. The client
connect is blocking, which is safe here: it completes via the listen
backlog without the accept task being polled.
Windows loopback absorbs large writes through a fast path regardless of
SO_SNDBUF/SO_RCVBUF, so pinning buffers -- before or after connect --
cannot make the write block there. Two CI rounds disproved both.

Keep the assertions that encode the actual contract (recovery stays
bounded, the loss marker survives, no retry is consumed) on every OS,
and gate only the elapsed-time claim to non-Windows, where backpressure
is observable.
…not block

Windows loopback absorbs multi-MB writes through a fast path that ignores
SO_SNDBUF/SO_RCVBUF. Pinning buffers after connect, then before connect,
both failed in CI: the write completes, recovery succeeds, and the loss
marker is cleared -- so gating only the timing assert just relocated the
failure to 'no entry found for key'.

The precondition is unreachable on Windows rather than violated, so mark
the test ignored there and keep it fully enforced on Linux and macOS.
@mfethe1 mfethe1 changed the title [lenny] fix(acp): pin socket buffers so write backpressure is OS-independent [lenny] fix(acp): skip the write-backpressure test where loopback cannot block Sep 11, 2026
@mfethe1

mfethe1 commented Sep 11, 2026

Copy link
Copy Markdown
Owner Author

CI attribution vs trunk baseline

Baseline: trunk run 34647837531 @ c644b8dc2e — 16 failing jobs.
This PR @ d90cf6797f — 10 failing jobs.

New regressions: none (comm -23 of the two failure sets is empty).

Flipped red → green:

  • Rust / Windows Rust (x86_64-pc-windows-msvc) ← the target of this PR
  • Windows Rust (x86_64-pc-windows-msvc)
  • Clients / Mobile, Mobile, Desktop, Desktop Domain / Desktop

Still red, unchanged from trunk: the relay/e2e cluster (Docker Hub pull access denied for minio/mc — infrastructure, not code) and Desktop Smoke E2E (3)/(4).

Why this shape of fix

Two earlier attempts on this branch tried to make the write block on Windows by pinning SO_SNDBUF/SO_RCVBUF (first after connect, then before connect via socket2). CI disproved both: Windows loopback absorbs multi-MB writes through a fast path that ignores those options. With the timing assert gated the failure simply moved to no entry found for key — because the write succeeded, recovery completed, and the loss marker was cleared.

So the precondition is unreachable on Windows, not violated. The test is now #[cfg_attr(windows, ignore)] and remains fully enforced on Linux and macOS (986 passed; 0 failed; 1 ignored locally). Final diff is 8 lines, test-only.

@mfethe1
mfethe1 merged commit 9c858b7 into product/main Sep 11, 2026
59 of 69 checks passed
@mfethe1
mfethe1 deleted the fix/windows-ws-backpressure-determinism branch September 11, 2026 23:04
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.

1 participant