Skip to content

Fixed data track streams dropping frames that arrive just before the end - #1025

Merged
davidliu merged 1 commit into
livekit:mainfrom
adrian-niculescu:fix-data-track-stream-end-race
Sep 16, 2026
Merged

davidliu merged 1 commit into
livekit:mainfrom
adrian-niculescu:fix-data-track-stream-end-race

Conversation

@adrian-niculescu

Copy link
Copy Markdown
Contributor

Collecting a subscribed data track's flow can complete without delivering anything: when the last frames and the end of the stream arrive right as collection starts, the collector sometimes gets none of them. DataTrackStreamTest.everyFrameSurvivesTheEndOfStream fails intermittently on main for this reason (1 of 30 runs here).

The drain sets drained and then emits its terminator, and onSubscription emits a terminator of its own when it reads drained as set. A collector that subscribes just before a short drain finishes already has the frames and the terminator buffered, but its onSubscription check runs after the flag was set, so its own terminator reaches takeWhile first. Pausing 5 ms at the start of that onSubscription makes every collection in the test come back empty.

The flag can't tell a collector whether the drain ended before or after it subscribed, but the replay cache can: a collector receives the replayed item atomically with its subscription. The drain now shares wrapped items with replay = 1, emitting Started, the frames, then Ended. A replayed frame predates the collector and is skipped, and a replayed Ended completes collection right away, which covers the late collector the flag was there for. The cost is that the replay cache holds the latest frame until the next item replaces it.

With the same 5 ms pause, the test class passes, and without it, it passed 30 runs in a row.

A collector that subscribed just before a short drain finished read the
drained flag as set and emitted its own terminator ahead of the frames and
terminator already buffered for it, so collection ended without them. The
drain now shares its items with a replay of one: a collector receives the
replayed item atomically with its subscription, which tells it whether a
frame predates it or the stream had already ended.
@changeset-bot

changeset-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b2dd327

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
client-sdk-android Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Copy link
Copy Markdown
Contributor

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

@davidliu davidliu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@davidliu
davidliu merged commit 66288fc into livekit:main Sep 16, 2026
6 checks passed
@davidliu davidliu mentioned this pull request Sep 16, 2026
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