Skip to content

fix: keep publish consistent with a reconnect - #32

Merged
dangusev merged 5 commits into
mainfrom
nash/rust-sdk-maintenance-92c8
Oct 14, 2026
Merged

dangusev merged 5 commits into
mainfrom
nash/rust-sdk-maintenance-92c8

Conversation

@cursor

@cursor cursor Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Two fixes for a publish that overlaps a reconnect (rejoin or migration). Each is its own commit with its own regression test.

1. publish uses the connection that is current when it gets the media lock

RtcCore::publish read the publisher connection before waiting for the media lock. If a reconnect replaced the connection while it waited, the publish negotiated with the old session. After a rejoin it failed even though the call had recovered. publish now reads the connection after taking the lock, like stop_publish, apply_publish_options and restart_publisher_ice already do. A side benefit: a publish queued behind leave now fails with publish() before join() and no longer negotiates on a torn-down connection. This mirrors the stale-negotiation fix in stream-video-swift (GetStream/stream-video-swift#1342).

2. Each publication gets one sender after a reconnect

A publish that ran after the connection swap but before restore_published_tracks had already put its sender on the new publisher. The restore then added a second transceiver for the same track. The SFU rejected the duplicate m-line, so the SDK rejoined again. The restore now skips tracks the publisher already sends, the same way the JS SDK reuses existing transceivers.

Tests

  • a_publish_that_waits_for_a_reconnect_negotiates_with_the_new_session: fails 10/10 without the fix and passes 30/30 with it. There is no timing dependence; the media lock orders the steps.
  • restoring_publications_adds_no_second_sender_for_a_track_published_meanwhile: failed with a duplicated sender before the fix.

Maintenance checks this run

  • The vendored SFU protobufs already match GetStream/protocol main; no proto changes.
  • OpenAPI v239.52 to v239.68 adds nothing for the public server-side video API that the SDK should type. New server setting groups still deserialize through CallSettingsResponse::additional_fields.
  • Dependencies: only semver-compatible updates are available and the advisories run is green, so the lockfile is unchanged.
  • Gate: cargo fmt --check, cargo clippy -D warnings (stable, as in CI), cargo test --locked (including the live suites), doc tests and cargo doc -D warnings all pass. The join_call example joins and leaves cleanly.
Open in Web View Automation 

Note

Medium Risk
Changes core publish/reconnect negotiation ordering and restore logic; wrong locking or sender dedup could break publishing or recovery after reconnect.

Overview
Fixes two race conditions when publish overlaps a reconnect (rejoin or migration).

RtcCore::publish now takes the media lock before resolving publisher handles, so negotiation uses the connection that is current when the call proceeds—not the session that was replaced while the call waited on the lock. That avoids failed publishes after a successful rejoin and aligns with stop_publish and related paths (a publish queued behind leave can fail with publish() before join() instead of negotiating on a torn-down PC).

restore_published_tracks skips adding a transceiver when the publisher already has a sender for that track ID, so a publish that ran after the connection swap does not get a duplicate m-line and trigger SFU rejection / another rejoin.

The changelog documents both fixes; regression tests record Twirp SetPublisher routing and assert a single sender per track after restore.

Reviewed by Cursor Bugbot for commit fd0e943. Bugbot is set up for automated code reviews on this repo. Configure here.

cursoragent and others added 5 commits October 14, 2026 07:22
A publish that waited for the media lock kept the connection it read
before waiting. If a reconnect swapped the connection meanwhile, the
publish negotiated with the replaced session. Read the connection once
the lock is held, as stop_publish and the other publisher paths do.

Co-authored-by: Neevash Ramdial (Nash) <mail@neevash.com>
A publish that ran between the connection swap and the restore had
already added its sender to the new publisher. The restore added a
second transceiver for the same track, the SFU rejected the
negotiation and the SDK rejoined again. Skip tracks the publisher
already sends.

Co-authored-by: Neevash Ramdial (Nash) <mail@neevash.com>
@dangusev
dangusev marked this pull request as ready for review October 14, 2026 11:16
@dangusev
dangusev merged commit e2b2e3a into main Oct 14, 2026
6 checks passed
@dangusev
dangusev deleted the nash/rust-sdk-maintenance-92c8 branch October 14, 2026 11:24
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