Skip to content

StreamEdge: move track resolution logic to TrackResolver class#538

Merged
dangusev merged 6 commits into
mainfrom
fix/track-added-race-condition
May 8, 2026
Merged

StreamEdge: move track resolution logic to TrackResolver class#538
dangusev merged 6 commits into
mainfrom
fix/track-added-race-condition

Conversation

@dangusev
Copy link
Copy Markdown
Collaborator

@dangusev dangusev commented May 8, 2026

Why

We need to match webrtc's "track_added" event and SFU's "TrackPublishedEvent" together to get the full info about a video track:

  • webrtc event contains track id
  • sfu event contains additional track metadata

Sometimes, StreamEdge track state becomes inconsistent which leads to TimeoutErrors on track resolution or "track not found in map" warnings when the track is already unpublished but wasn't resolved in first place.

In this PR, I'll try to address this race conditions.

Changes

  • consolidate the stateful logic with pending tracks in TrackResolver
  • Cancel the resolving process when a user leaves the call
  • log debug instead of warning when unresolved track is removed - there's nothing to do about it anyway.

- consolidate the stateful logic with pending tracks in TrackResolver
- cancel resolving process when a user leaves the call
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d939a3f1-f390-4442-8e20-f5d91874289d

📥 Commits

Reviewing files that changed from the base of the PR and between b5cd145 and 59f6ba2.

📒 Files selected for processing (1)
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py

📝 Walkthrough

Walkthrough

This PR extracts WebRTC-to-SFU track correlation logic from StreamEdge into a new TrackResolver class. TrackResolver holds track mappings, a pending WebRTC callback registry, per-key resolve locks, and pending eviction via pending_ttl. resolve() reuses known mappings, migrates on session change, or polls pending entries with timeout/cancel semantics; register(), unpublish(), and cancel() implement corresponding lifecycle operations. StreamEdge now delegates track_added registration, track published resolution, unpublish, and cancel to TrackResolver. Async tests exercise timing, migration, anonymous fallback, eviction, concurrent resolves, and cancellation.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 11c6b42f-d6ad-4bc3-a0c9-e72c9091dcf5

📥 Commits

Reviewing files that changed from the base of the PR and between 0c684e5 and a0808ac.

📒 Files selected for processing (3)
  • plugins/getstream/tests/test_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py

Comment thread plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py (4)

34-47: 💤 Low value

Collapse redundant elif/else.

Both elif and else return "video", so the explicit branch is dead.

Refactor
 def _get_webrtc_kind(stream_track_type: StreamTrackType.ValueType) -> str:
     """Get the expected WebRTC kind (audio/video) for an SFU track type."""
     if stream_track_type in (
         StreamTrackType.TRACK_TYPE_AUDIO,
         StreamTrackType.TRACK_TYPE_SCREEN_SHARE_AUDIO,
     ):
         return "audio"
-    elif stream_track_type in (
-        StreamTrackType.TRACK_TYPE_VIDEO,
-        StreamTrackType.TRACK_TYPE_SCREEN_SHARE,
-    ):
-        return "video"
-    else:
-        return "video"
+    return "video"

97-106: 💤 Low value

Reuse path rewrites the map entry unnecessarily.

_reuse_known already flips entry.published = True on the existing object, then resolve() constructs a fresh _TrackEntry and reassigns self._track_map[track_key]. Functionally identical, but the double-write is a minor smell. Either drop the mutation in _reuse_known (let line 105 own it) or skip the rewrite when reused.


148-163: 💤 Low value

Single-shot migration leaves additional stale sessions in the map.

The loop returns on the first match. If a user has accumulated multiple stale _TrackKeys for the same stream_track_type (rapid reconnects), only one is migrated/deleted; the rest linger until process exit. Probably benign, but consider deleting all matches and returning the most recent.


234-247: 💤 Low value

TTL eviction logged at warning — possibly noisy on participant churn.

Stale pending eviction is the expected outcome when a participant leaves before SFU confirms a track that was registered but cancel() didn't cover (e.g., race between track_added and departure). Per the PR description ("Change logging for unresolved-track removal from warning to debug"), consider logger.debug here unless eviction genuinely indicates a bug.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 255b215d-e080-430d-bcc8-8117e46f08fa

📥 Commits

Reviewing files that changed from the base of the PR and between a0808ac and d44dc7c.

📒 Files selected for processing (2)
  • plugins/getstream/tests/test_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
plugins/getstream/tests/test_track_resolver.py (1)

7-7: ⚡ Quick win

Import TrackResolver via the public package API, not _track_resolver.

Line 7 imports from a private module path, which couples tests to internal module structure. Re-export TrackResolver from the package and import from that public path.

Proposed change
-from vision_agents.plugins.getstream._track_resolver import TrackResolver
+from vision_agents.plugins.getstream import TrackResolver
# vision_agents/plugins/getstream/__init__.py
from ._track_resolver import TrackResolver

As per coding guidelines: "Never import from private modules (_foo) outside of the package's own __init__.py. Use the public re-export instead."


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 27e497f3-1bef-43ca-99ef-82eeea1e77c8

📥 Commits

Reviewing files that changed from the base of the PR and between 4f32b67 and f712cd2.

📒 Files selected for processing (3)
  • plugins/getstream/tests/test_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/getstream/vision_agents/plugins/getstream/_track_resolver.py
  • plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py

@dangusev dangusev merged commit 57d0923 into main May 8, 2026
6 checks passed
@dangusev dangusev deleted the fix/track-added-race-condition branch May 8, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants