Skip to content

Send reliable data after a full reconnect instead of dropping it - #1277

Open
hiroshihorie wants to merge 3 commits into
hiroshi/fix-buffer-status-waitfrom
hiroshi/reliable-sends-full-reconnect
Open

hiroshihorie wants to merge 3 commits into
hiroshi/fix-buffer-status-waitfrom
hiroshi/reliable-sends-full-reconnect

Conversation

@hiroshihorie

@hiroshihorie hiroshihorie commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Top of stack 1276, on #1267.

A reliable data packet sent while the engine does a full reconnect was reported as sent and never went out. sendDataPacket parked it for the resume replay, and a full reconnect never runs that replay. publishData, RPC and data stream writes all hit this. Found while checking Devin's finding on #1267.

A second loss had the same shape on a resume: a packet sent after the reconnect response had replayed the resend buffer, but before the resume finished, was parked and never replayed again. Both are fixed here.

Change

  • A reliable send waits the reconnect out, then goes out on the connection the reconnect leaves. It fails with UnexpectedConnectionState if the engine closes instead, which is how a reconnect that gives up ends.
  • Every wait happens before the sequence number is taken. A send that a full reconnect interrupts while it waits for the publisher goes round again instead of failing with Publisher connection reset. Nothing awaits between the last reconnect check and the send, so a reliable packet is no longer parked.
  • A full reconnect restarts the reliable sequence at 1 and clears the resend buffer.
  • Lossy packets are still dropped during a reconnect rather than sent late.
  • The wait uses Fix waiting for the data channel send buffer to drain #1267's state change signal, so a send resumes as soon as the reconnect ends.

Behavior change for callers

Reliable publishData, RPC requests and responses, and data stream writes made during a reconnect now wait for the whole reconnect, which can take tens of seconds across retries, and throw UnexpectedConnectionState if it gives up. Before, they returned at once and the packet was lost. An RPC caller's own timeout can fire while the request is still held, in which case the callee may still run the handler after the reconnect. The changeset says so.

Other SDKs

client-sdk-js client-sdk-swift rust-sdks
Reliable send during a full reconnect parked, never replayed, reported as sent queued sends fail with a cancelled error, later sends wait for the new channel waits for the reconnect, then sends on the new session
Sequence and resend buffer on a full reconnect reset reset new per session

For reliable sends this follows rust-sdks, where publish_data holds the reconnect lock's read guard. rust-sdks holds lossy packets too, this PR does not. client-sdk-js has the same loss Flutter had.

Tests

  • test/core/reliable_send_reconnect_test.dart: a send during a full reconnect reaches the new channel only after the reconnect finishes, sequence numbers restart at 1, a waiting send fails when the engine closes, a resume still sends once, and a send made after the reconnect response but before the resume ends goes out. All but the resume-sends-once test fail against Fix waiting for the data channel send buffer to drain #1267's lib.
  • Full unit suite: 531 passed, 1 skipped. flutter analyze, dart analyze web/, format, import order and version check pass.
  • livekit/e2e-flutter#20 adds the scenario "Reliable data during a full reconnect", run against a local livekit-server --dev. A sender publishes a numbered reliable packet every 20 ms and the server forces a full reconnect partway. It is marked as an expected failure there until this PR merges, so drop that marker afterwards.
SDK Result
#1268 (2cc480e) fail twice: 2 and 7 packets reported sent and never arrived
#1267 (daf740d) fail: 9 reported sent and never arrived, 1 send failed with Publisher connection reset
this PR (dc41370) pass 3 of 3, every packet arrived in order
  • e2e-flutter#22's "Reliable data across a resume" fails on main and on Fix waiting for the data channel send buffer to drain #1267, and passes 2 of 2 on this PR, which is the resume window above.
  • On this head, the scenarios tagged reconnect, data and signaling pass, apart from the expected failures and one flaky NodeFailure run that times out before the failure is simulated, on main as well.

sendDataPacket parked a reliable packet sent during any reconnect for the
resume replay. A full reconnect never runs that replay, so the packet was
reported as sent and never went out. publishData, RPC and data stream
writes all hit this.

A reliable send now waits the reconnect out and goes out on the connection
it leaves, the way rust-sdks waits for reconnection in publish_data. It
fails with UnexpectedConnectionState if the engine closes instead. All
waits happen before the sequence number is taken, a send that a full
reconnect interrupts while it waits for the publisher goes round again
instead of failing, and nothing awaits between the last reconnect check
and the send. A full reconnect restarts the reliable sequence and clears
the resend buffer, as client-sdk-js, client-sdk-swift and rust-sdks do for
a new session. Lossy packets are still dropped during a reconnect rather
than sent late.
@hiroshihorie
hiroshihorie added this pull request to stack #1276 November 3, 2026 07:18
@hiroshihorie
hiroshihorie marked this pull request as ready for review November 3, 2026 07:19

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

A reliable send made after the reconnect response had replayed the
resend buffer, but before the resume finished, was parked in that buffer
and never sent, because nothing replayed it again. This branch already
fixes it (such sends wait for the reconnect to end), but no unit test
covered it. The new test holds the primary connection in connecting so
the resume is still running after its response, sends, and checks the
packet goes out once the resume ends. It fails on the branch below.

The changeset now says that reliable sends wait for the reconnect and
fail with UnexpectedConnectionState if the room disconnects instead.

This branch has not been deployed

No deployments
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.

2 participants