fix(core): single-flight Call.join to stop concurrent-join race - #1764
fix(core): single-flight Call.join to stop concurrent-join race#1764PratimMallick wants to merge 2 commits into
Conversation
Coalesce overlapping join() callers onto one in-flight attempt and clean up sessions that fail to connect, preventing SFU-evicted zombie publishers. Co-authored-by: Cursor <cursoragent@cursor.com>
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
Walkthrough
ChangesCall join coordination
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ConcurrentCallers
participant CallJoinCoordinator
participant executeJoin
participant API
participant SFUConnection
ConcurrentCallers->>CallJoinCoordinator: call join
CallJoinCoordinator->>executeJoin: execute one join
executeJoin->>API: request join
executeJoin->>SFUConnection: connect once
CallJoinCoordinator-->>ConcurrentCallers: share join result
sequenceDiagram
participant executeJoin
participant SFUConnection
participant CallJoinCoordinator
participant Session
executeJoin->>SFUConnection: report terminal failure
executeJoin->>CallJoinCoordinator: discard failed session
CallJoinCoordinator->>Session: clear and clean up session
CallJoinCoordinator-->>executeJoin: return failure
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/components/CallJoinCoordinator.kt`:
- Line 390: Update executeJoin’s failed-join cleanup so it clears the active
session only when it is still the same localSession; preserve any replacement
installed by discardFailedSession during recovery. Add a recovery-failure test
that installs a replacement session before returning Failure and verifies the
replacement remains active.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5b98171e-ff06-4133-a27e-e543cf2d2d64
📒 Files selected for processing (2)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/components/CallJoinCoordinator.ktstream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/components/CallJoinCoordinatorTest.kt
SDK Size Comparison 📏
|
Remove the discardFailedSession ownership guard. Once join is returning Failure (including after failed join-time recovery), clear the active slot and cleanup both the join session and any reconnect replacement. Co-authored-by: Cursor <cursoragent@cursor.com>
|


Goal
closes AND-1379
Prevent overlapping
Call.join()calls from creating multipleRtcSessions that share the samesessionId. The SFU keeps only the latest participant and evicts the others, which leaves zombie publishers that cannot publish A/V and often fail subsequent RPCs withPARTICIPANT_NOT_FOUND, triggering reconnect/rejoin loops.This addresses a class of Android reconnect / join-storm failures observed when the app (or SDK paths such as lobby +
CallActivity/ auto-join) invokejoin()concurrently on the sameCallinstance. The previoussession.value != nullguard is insufficient because the session is only set after the coordinator round-trip.Implementation
CallJoinCoordinator.join()viaMutex+CompletableDeferred: the first caller runs the join; concurrent callers await the sameResult.discardFailedSession()clears the active session and callsRtcSession.cleanup()so failed sessions do not keep issuing RPCs after eviction.Testing
./gradlew :stream-video-android-core:spotlessApply./gradlew :stream-video-android-core:testDebugUnitTest --tests 'io.getstream.video.android.core.call.components.CallJoinCoordinatorTest'— passedjoin()→ 5×joinInternalwithin ~7ms, multipleRtcSessions, publisher ICECONNECTED → DISCONNECTED/ stuckCHECKING, unable to publish A/VSingle-flight: join already in flightand share one sessionFailure modes this mitigates
☑️Contributor Checklist
General
developbranchCode & documentation
stream-video-examples)☑️Reviewer Checklist
🎉 GIF
N/A — core join orchestration fix, no UI changes.
Made with Cursor
Summary by CodeRabbit
Bug Fixes
Reliability