Skip to content

Bump the rtc pin to a1566b7 (send rejection + ordered default) - #131

Merged
lann merged 1 commit into
mainfrom
rtc-pin-bump
Jul 29, 2026
Merged

Bump the rtc pin to a1566b7 (send rejection + ordered default)#131
lann merged 1 commit into
mainfrom
rtc-pin-bump

Conversation

@lann

@lann lann commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Moves the [patch.crates-io] rev from a10cd2c to a1566b7 — the two upstream data-channel fixes landed since our pin, both silent-data-loss bugs.

What's in the bump

webrtc-rs/rtc#138 — reject sends the write path cannot carry out. RTCDataChannel::send only checked that the channel was registered (true from create_data_channel on). Whether its SCTP stream existed was checked later in handle_write, on the pipeline's write pass, where an Err is logged and discarded — so the caller got Ok(()) for a message dropped on the floor. Now returns ErrDataChannelNotOpen while connecting, ErrDataChannelClosed once gone.

webrtc-rs/rtc#140ordered defaults to true. RTCDataChannelInit derived Default, so ordered came out false, contradicting its doc comment and the W3C dictionary. We don't take that path (wasip3-impl always passes ordered explicitly and defaults its own config to true), so this is a latent-trap fix rather than a behavior change here.

Why it matters beyond the changelog

#138 also stops a rejected send from charging outstanding_bytes — bytes that never entered the SCTP pipeline, so nothing ever released them. That counter is exactly what the in-guest close drain polls (wasip3-impl/src/runtime.rs:397, channel_outstanding_bytes(id) == 0) to decide a channel has flushed. A leaked counter means the predicate can never go true and every close burns its full bounded window — which is the "every close pays the full grace even when nothing is queued" symptom described in #126. Worth re-measuring close latency against that issue once this lands.

Risk

#138 changes send's success contract: a send on a connecting channel now errors where it previously reported success. Upstream is explicit that this diverges from W3C (which prescribes buffering), calling it the smallest step that stops the silent loss. The exposed surface here is the wasip3 provider's error mapping and any test that sends before open — post-close-send and the open/send-race shapes.

Verification

Local, on this rev: just clippy, just test, just examples::test-webrtc-composed green; both direct rtc consumers pass the full loopback corpus 37/37 (just conformance::wasip3, just conformance::wasmtime).

Not covered locally: the interop pairs and the browser/Node targets — left to CI. Note the reference-x-* channel-close-flush shapes are the known flake family (#117, #124), so a red matrix on those is not necessarily this bump.

Sequencing

#116 also moves this pin — to lann/rtc rev 353a84a (= old a10cd2c + the lann/rtc#1 reset-reassembly cherry-pick, still not upstreamed). These two will conflict. Suggested order: land #116 first, then rebase its fork onto a1566b7 and rebase this away; or land this and re-cut the fork from a1566b7.

Related: #120 (unwind the pin entirely) stays blocked — upstream's latest release is still v0.20.0-rc.4, which predates even the old pin.

Moves the [patch.crates-io] rev from a10cd2c to a1566b7, picking up the two
upstream data-channel fixes landed since:

  * webrtc-rs/rtc#138 — RTCDataChannel::send returned Ok(()) for a message the
    write path then discarded (channel connecting, or already closed), and
    charged outstanding_bytes that nothing would release. That counter is what
    the in-guest close drain polls to decide a channel has flushed.
  * webrtc-rs/rtc#140 — RTCDataChannelInit::ordered defaulted to false against
    its own docs and the W3C dictionary.

Note #138 changes send's contract: a send on a connecting channel now returns
ErrDataChannelNotOpen where it previously reported success.
@lann

lann commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

CI matrix is red, but not from this change. Both runs failed on the same cell — reference-x-jco-node [loopback] / channel-close-flush — with two different shapes (answerer: receive: closed, then answerer: add-ice-candidate: closed), which is the signature of the known flake family.

Two reasons it can't be the pin:

  1. Neither peer in that pair uses rtc. reference is Google's libwebrtc via LiveKit's bindings; jco-node is node-datachannel. The [patch.crates-io] rev only reaches the Rust stacks (wasmtime via webrtc, wasip3-guest directly).

  2. main fails the same way. The last six conformance runs on main: 3 failures, all channel-close-flush, all on reference* cells —

    run failure
    30317826410 reference / channel-close-flush — answerer: channel closed before open
    30316984281 reference-x-jco-node / channel-close-flush — answerer: receive: closed
    30214541517 reference / channel-close-flush — answerer: channel closed before open

    These are webrtc 0.20 driver delivers data-channel close ahead of already-received messages (E18 bug 3) #117 (webrtc driver ordering), node-datachannel drops messages queued behind a remote close #124 (node-datachannel TSFN abort), and the two bugs Fix two of E18's three close-flush data-loss bugs #116 fixes but that aren't on main yet.

What did carry signal: the only failing cell in both runs was that one pair — every wasmtime and wasip3-guest row, and every interop pair involving them, passed, as did shadow-lab (non-loopback, deterministic) and rust-checks. Combined with 37/37 locally on both rtc-consuming targets, there's no evidence of regression from #138's send-contract change.

Landing #116 should take this pair's flake rate down and make the matrix a useful gate again.

@lann
lann merged commit 2f12c31 into main Jul 29, 2026
6 of 8 checks passed
@lann
lann deleted the rtc-pin-bump branch July 29, 2026 02:45
@lann

lann commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Correction to my "Why it matters beyond the changelog" claim above: the outstanding_bytes leak fix in webrtc-rs/rtc#138 has no effect on this repo's close drains. I measured it rather than leaving it as a guess.

Instrumented the wasip3 channel drain (runtime.rs:397) and ran channel-close-flush on both revs:

pin drain exit waited
a10cd2c (old) flushed (outstanding == 0) 44, 47, 47, 54 ms
a1566b7 (new) flushed (outstanding == 0) 17, 26, 45, 45 ms

Unchanged — the difference is run-to-run noise, and neither approaches the 1 s CHANNEL_CLOSE_FLUSH_BOUND.

The reason there was never a leak to fix here: provider.rs's send and send_via_stream both funnel through when_open(), which returns Error::Closed when the channel is closed and awaits while it is opening. rtc's rejected-send path — the one that used to charge outstanding_bytes and return Ok(()) — is unreachable from this provider.

That does not weaken the case for the bump: #138 still closes a real silent-loss hole for any consumer that doesn't gate sends the way this provider does, and #140 removes a latent trap for anyone using RTCDataChannelInit::default(). It just isn't a #126 fix, and I've corrected the record there too (with the finding that #126's actual scope is the Wasmtime host's unconditional tokio::time::sleep(CLOSE_DRAIN), not the wasip3 pump, whose drain already exits early).

lann added a commit that referenced this pull request Aug 6, 2026
rtc 0.20.0 shipped on crates.io (2026-07-31) as a strict descendant of the
pinned commit a1566b7, so all three fixes the [patch.crates-io] redirect
existed for — the srflx source-address fix (webrtc-rs/rtc#136), send
rejection on unwritable channels (#138), and the ordered default (#140) —
are in the release, along with the earlier empty-message receive fix
(#131). Drop the patch, bump the workspace rtc pin and the Wasmtime
host's webrtc dependency from 0.20.0-rc.4 to 0.20.0.

The 17 commits between the pin and the release are DTLS
fingerprint/cipher-suite fixes, a TURN ICE-restart fix, the interceptor
object-safety refactor (media path; no data-channel API change), docs,
and an rkyv bump.

Gates: just check, just test, examples::test-webrtc-composed, and the
full conformance run (loopback + interop, 304 results, 0 failing,
matrices unchanged) all pass.

Closes #120.
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