Skip to content

fix(llc): fixed ringing race conditions#1185

Open
Brazol wants to merge 4 commits intomainfrom
fix/ringing-fixes
Open

fix(llc): fixed ringing race conditions#1185
Brazol wants to merge 4 commits intomainfrom
fix/ringing-fixes

Conversation

@Brazol
Copy link
Contributor

@Brazol Brazol commented Mar 10, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Fixed a race condition when joining a call while another connection is in progress; improved timeout handling and clearer failure reporting.
    • Ensured coordinator connection is established before consuming incoming calls during cold start to avoid missed or failed accepts.
    • [iOS] Prevented loss of push notification events when the listener isn't yet registered by queuing and delivering pending events.

@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

📝 Walkthrough

Walkthrough

Fixes race conditions: Call.join now waits for a non-connecting state with explicit timeout handling; consumeAndAcceptActiveCall ensures coordinator WebSocket is connected before consuming and accepting incoming calls; iOS push plugin buffers events until an EventChannel listener registers.

Changes

Cohort / File(s) Summary
Changelog updates
packages/stream_video/CHANGELOG.md, packages/stream_video_push_notification/CHANGELOG.md
Added Upcoming sections documenting the fixes: Call.join timeout/race, coordinator WS pre-connect for active calls, and iOS push event buffering.
Call state / join logic
packages/stream_video/lib/src/call/call.dart
Replaced status-specific wait with a condition-based firstWhere for non-Connecting/Joining states within connectTimeout; added explicit TimeoutException handling and clearer logging for ongoing connect success/failure/timeout.
Active call consumption flow
packages/stream_video/lib/src/stream_video.dart
Added pre-connect step to ensure coordinator WebSocket is connected before consuming calls; refactored consume/accept handling to sequential checks with explicit failure logging and controlled early returns.
iOS push event buffering
packages/stream_video_push_notification/ios/.../StreamVideoPushNotificationPlugin.swift
Introduced pendingEvents queue; send appends when no eventSink, and onListen flushes queued events to the newly attached eventSink; added guard for plugin liveness before emitting.

Sequence Diagram(s)

sequenceDiagram
  participant SV as StreamVideo
  participant WS as CoordinatorWS
  participant Call as CallService
  participant App as AppHandler

  rect rgba(200,220,255,0.5)
    SV->>WS: ensure connect()
    alt connect success
      SV->>WS: consumeActiveCall()
      alt consume returns call data
        SV->>Call: create/lookup Call object
        SV->>Call: accept()
        alt accept success
          Call-->>SV: accepted
          SV->>App: onCallAccepted(call)
        else accept failure
          Call-->>SV: accept failed
          SV->>App: log error / return false
        end
      else no call or consume failure
        WS-->>SV: no call / error
        SV->>App: return false
      end
    else connect failure
      WS-->>SV: connect failed
      SV->>App: log error / return false
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I buffered the hops and timed each leap,
Waiting for webs and calls not to creep,
I flushed my queue when a listener woke,
Accepted the ring and steadied the yolk,
Thump-thump—race bugs hid while I munched a leek.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request has no description provided by the author, missing all required sections from the template including Goal, Implementation details, Testing, and Contributor Checklist. Add a comprehensive description following the template with Goal, Implementation details, Testing explanation, and complete the Contributor Checklist including assignment and Slack notification.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(llc): fixed ringing race conditions' clearly and specifically summarizes the main change across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/ringing-fixes

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.

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 5.88235% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.36%. Comparing base (45519e3) to head (eaf1842).

Files with missing lines Patch % Lines
packages/stream_video/lib/src/call/call.dart 11.11% 16 Missing ⚠️
packages/stream_video/lib/src/stream_video.dart 0.00% 16 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1185      +/-   ##
========================================
- Coverage   6.36%   6.36%   -0.01%     
========================================
  Files        615     615              
  Lines      43345   43362      +17     
========================================
+ Hits        2757    2758       +1     
- Misses     40588   40604      +16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Brazol Brazol marked this pull request as ready for review March 11, 2026 12:16
@Brazol Brazol requested a review from a team as a code owner March 11, 2026 12:16
Copy link

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stream_video/lib/src/call/call.dart`:
- Around line 839-861: The wait for an ongoing connect treats transient join
states as failures; update the predicate used with state.firstWhere in join() so
it treats CallStatusConnecting, CallStatusJoining and CallStatusJoined as
in‑flight (i.e. continue waiting) and only resolves when a terminal state is
reached (e.g. CallStatusConnected or a terminal failure). In practice change the
firstWhere check around state.firstWhere(...) so it excludes Connecting, Joining
and Joined (rather than only Connecting), then keep the existing handling that
treats CallStatusConnected as success and other terminal states as error; this
prevents _performJoinCallRequest/_startSession races and avoids spuriously
returning "ongoing connect failed".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c8e483eb-2290-4b88-998a-8ac7d0ffcadb

📥 Commits

Reviewing files that changed from the base of the PR and between 45519e3 and f90e221.

📒 Files selected for processing (5)
  • packages/stream_video/CHANGELOG.md
  • packages/stream_video/lib/src/call/call.dart
  • packages/stream_video/lib/src/stream_video.dart
  • packages/stream_video_push_notification/CHANGELOG.md
  • packages/stream_video_push_notification/ios/stream_video_push_notification/Sources/stream_video_push_notification/StreamVideoPushNotificationPlugin.swift

@Brazol Brazol changed the title fix(llc): fixed ringing race when terminated iOS fix(llc): fixed ringing race conditions Mar 11, 2026
Copy link

@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)
packages/stream_video/lib/src/call/call.dart (1)

844-849: Wait condition may resolve prematurely on transient reconnection states.

The predicate is! CallStatusConnecting && is! CallStatusJoining resolves when status transitions to CallStatusReconnecting or CallStatusMigrating. These are transient states (per call_status.dart lines 30-42) that can be set by lifecycleCallConnecting() during reconnection flows. Since neither matches the success condition (Connected || Joined), this returns "ongoing connect failed" even though the reconnection may ultimately succeed.

Consider waiting for terminal states instead:

♻️ Suggested approach: wait for terminal states
         final currentState = await state.firstWhere(
           (it) =>
-              it.status is! CallStatusConnecting &&
-              it.status is! CallStatusJoining,
+              it.status is CallStatusConnected ||
+              it.status is CallStatusDisconnected ||
+              it.status is CallStatusReconnectionFailed,
           timeLimit: _stateManager.callState.preferences.connectTimeout,
         );

-        if (currentState.status is CallStatusConnected ||
-            currentState.status is CallStatusJoined) {
+        if (currentState.status is CallStatusConnected) {
           _logger.v(() => '[join] ongoing connect succeeded');
           return const Result.success(none);
         } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/lib/src/call/call.dart` around lines 844 - 849, The
current wait uses a predicate that treats any non-Connecting/Joining state as
done and therefore returns prematurely for transient states like
CallStatusReconnecting or CallStatusMigrating (see call_status.dart and
lifecycleCallConnecting); update the firstWhere predicate on state so it only
completes when the call reaches true terminal outcomes (e.g.,
CallStatusConnected or CallStatusJoined, and optionally terminal failure/ended
statuses your domain defines) instead of negating Connecting/Joining, ensuring
Reconnecting and Migrating do not satisfy the condition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/stream_video/lib/src/call/call.dart`:
- Around line 844-849: The current wait uses a predicate that treats any
non-Connecting/Joining state as done and therefore returns prematurely for
transient states like CallStatusReconnecting or CallStatusMigrating (see
call_status.dart and lifecycleCallConnecting); update the firstWhere predicate
on state so it only completes when the call reaches true terminal outcomes
(e.g., CallStatusConnected or CallStatusJoined, and optionally terminal
failure/ended statuses your domain defines) instead of negating
Connecting/Joining, ensuring Reconnecting and Migrating do not satisfy the
condition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72be25b3-7321-48d0-9321-781e05bf424b

📥 Commits

Reviewing files that changed from the base of the PR and between f90e221 and eaf1842.

📒 Files selected for processing (1)
  • packages/stream_video/lib/src/call/call.dart

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