Skip to content

fix(llc): connection flickering rejoin#1236

Merged
Brazol merged 4 commits into
mainfrom
fix/connection-flickering-rejoin
May 29, 2026
Merged

fix(llc): connection flickering rejoin#1236
Brazol merged 4 commits into
mainfrom
fix/connection-flickering-rejoin

Conversation

@Brazol
Copy link
Copy Markdown
Contributor

@Brazol Brazol commented May 28, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed connection flickering that could cause rejoin flow failures.
    • Improved reconnection reliability by verifying network stability before attempting to rejoin or migrate calls.
  • Tests

    • Added tests validating reconnect behavior, stability-window handling, and network-availability budgeting.

Review Change Stack

@Brazol Brazol requested a review from a team as a code owner May 28, 2026 13:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Warning

Review limit reached

@Brazol, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 22 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33b84ea3-cad0-485c-b3fb-3b5017d44488

📥 Commits

Reviewing files that changed from the base of the PR and between 843216d and 1923ee4.

📒 Files selected for processing (2)
  • packages/stream_video/CHANGELOG.md
  • packages/stream_video/lib/src/call/call.dart
📝 Walkthrough

Walkthrough

This PR adds a 3s stability window for rejoin/migrate reconnection flows, refactors _awaitNetworkAvailable into a bounded, retrying wait that enforces stability and an overall availability budget, and adds tests validating fast/rejoin/migrate behaviors and budgeted failure.

Changes

Network Stability on Reconnection

Layer / File(s) Summary
Core reconnection changes
packages/stream_video/CHANGELOG.md, packages/stream_video/lib/src/call/call.dart
_reconnect computes a stabilityWindow (3s for rejoin/migrate, zero otherwise) and calls a refactored _awaitNetworkAvailable that adjusts network monitor interval, enforces a total availability budget, waits for InternetStatus.connected while racing against call-leave, and (when stabilityWindow > 0) verifies the connection remains stable for the full window before returning; otherwise it retries until the budget is exhausted.
Reconnect stability tests
packages/stream_video/test/src/call/call_reconnect_stability_test.dart
Adds a test suite that mocks session factory and network status to validate: fast reconnects proceed immediately; rejoin/migrate wait for the 3s stability window; stability restarts on mid-window drops; and rejoin can fail when the network availability budget is exhausted.

Sequence Diagram

sequenceDiagram
  participant Reconnect as _reconnect
  participant AwaitNet as _awaitNetworkAvailable
  participant Monitor as NetworkMonitor
  participant Status as InternetStatus
  participant Lifecycle as Call Lifecycle
  Reconnect->>Reconnect: Compute stabilityWindow (3s for rejoin/migrate)
  Reconnect->>AwaitNet: Call with stabilityWindow
  AwaitNet->>Monitor: Adjust polling to offline-check interval
  AwaitNet->>AwaitNet: Compute total time budget
  loop until budget exhausted or stable
    AwaitNet->>Status: Wait for InternetStatus.connected
    Status->>AwaitNet: Connected / NotConnected
    alt Connected
      AwaitNet->>Status: Observe stability for stabilityWindow
      Status->>AwaitNet: Stable / Disconnect observed
      alt Stable
        AwaitNet->>Reconnect: Return connected
      else Disconnect
        AwaitNet->>AwaitNet: Log instability and retry
      end
    end
    AwaitNet->>Lifecycle: Race against call-leave completion
  end
  AwaitNet->>Monitor: Restore original interval
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through logs and network mist,
Waiting three seconds, I clenched my fist.
When signals stayed steady, I danced with glee—
Rejoin and migrate now wait patiently.
No more flicker-chaos beneath the willow tree.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is empty. The template requires Goal, Implementation details, Testing, and Contributor Checklist sections. Add a comprehensive description covering the goal, implementation details, testing approach, and complete the contributor checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(llc): connection flickering rejoin' directly relates to the main change: fixing rejoin failures caused by connection flickering.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/connection-flickering-rejoin

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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 83.78378% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 9.10%. Comparing base (2ffb34a) to head (1923ee4).

Files with missing lines Patch % Lines
packages/stream_video/lib/src/call/call.dart 83.78% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1236      +/-   ##
========================================
+ Coverage   9.01%   9.10%   +0.09%     
========================================
  Files        676     676              
  Lines      49585   49607      +22     
========================================
+ Hits        4469    4518      +49     
+ Misses     45116   45089      -27     

☔ 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.

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: 0

🧹 Nitpick comments (2)
packages/stream_video/test/src/call/call_reconnect_stability_test.dart (2)

73-75: ⚡ Quick win

Consider adding explicit Call cleanup in tearDown.

While the test framework may handle cleanup automatically, explicitly calling call.leave() on any created Call instances would ensure proper resource disposal and prevent potential memory leaks in the test suite.

♻️ Suggested enhancement
+    late Call? call;
+
     setUp(() {
       internetStatusController = BehaviorSubject<InternetStatus>.seeded(
         InternetStatus.connected,
       );
+      call = null;
       // ... rest of setup
     });

     tearDown(() async {
+      await call?.leave();
       await internetStatusController.close();
     });

Then in each test, assign call = buildCall(...) so tearDown can clean it up.

🤖 Prompt for 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.

In `@packages/stream_video/test/src/call/call_reconnect_stability_test.dart`
around lines 73 - 75, Add explicit Call cleanup in the tearDown by tracking the
Call instance created in tests (assign call = buildCall(...) inside each test)
and invoking await call.leave() (or call.dispose() if applicable) before closing
other resources; update the tearDown to check if the tracked Call is non-null
and call await call.leave() then await internetStatusController.close() so Call
resources are deterministically released.

337-375: ⚡ Quick win

Consider adding a test verifying migrate proceeds after the stability window.

Test coverage for migrate currently only validates that it doesn't proceed early (within 500ms), but there's no corresponding test verifying it eventually proceeds after the full 3s stability window elapses. The rejoin strategy has this coverage via the pairing of test 2 (doesn't proceed early) and test 3 (proceeds after window). Adding a similar positive-case test for migrate would ensure symmetric coverage.

✅ Suggested test to add
test(
  'migrate reconnect proceeds once the network stays connected for the '
  'stability window',
  () async {
    final call = buildCall();

    await call.join();
    expect(capturedCallback, isNotNull);

    capturedCallback!(mockPc, SfuReconnectionStrategy.migrate);

    // Wait past the 3s stability window with the network steady.
    await Future<void>.delayed(const Duration(milliseconds: 3500));

    // Initial join + migrate = 2 joinCall invocations.
    // For migrate, migratingFrom should be set to the current SFU name.
    verify(
      () => coordinatorClient.joinCall(
        callCid: any(named: 'callCid'),
        ringing: any(named: 'ringing'),
        create: any(named: 'create'),
        migratingFrom: any(named: 'migratingFrom'),
        migratingFromList: any(named: 'migratingFromList'),
        video: any(named: 'video'),
        membersLimit: any(named: 'membersLimit'),
      ),
    ).called(2);
  },
  timeout: const Timeout(Duration(seconds: 10)),
);
🤖 Prompt for 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.

In `@packages/stream_video/test/src/call/call_reconnect_stability_test.dart`
around lines 337 - 375, Add a positive-case test mirroring the rejoin
stability-window test: create a test (e.g. "migrate reconnect proceeds once the
network stays connected for the stability window") that builds the call via
buildCall(), awaits call.join(), asserts capturedCallback is non-null, invokes
capturedCallback!(mockPc, SfuReconnectionStrategy.migrate), then awaits past the
3s stability window (e.g. Future.delayed 3500ms) and verifies
coordinatorClient.joinCall was called twice (initial join + migrate) and that
migratingFrom is set as expected; include a longer test timeout (e.g.
Timeout(Duration(seconds:10))).
🤖 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.

Nitpick comments:
In `@packages/stream_video/test/src/call/call_reconnect_stability_test.dart`:
- Around line 73-75: Add explicit Call cleanup in the tearDown by tracking the
Call instance created in tests (assign call = buildCall(...) inside each test)
and invoking await call.leave() (or call.dispose() if applicable) before closing
other resources; update the tearDown to check if the tracked Call is non-null
and call await call.leave() then await internetStatusController.close() so Call
resources are deterministically released.
- Around line 337-375: Add a positive-case test mirroring the rejoin
stability-window test: create a test (e.g. "migrate reconnect proceeds once the
network stays connected for the stability window") that builds the call via
buildCall(), awaits call.join(), asserts capturedCallback is non-null, invokes
capturedCallback!(mockPc, SfuReconnectionStrategy.migrate), then awaits past the
3s stability window (e.g. Future.delayed 3500ms) and verifies
coordinatorClient.joinCall was called twice (initial join + migrate) and that
migratingFrom is set as expected; include a longer test timeout (e.g.
Timeout(Duration(seconds:10))).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28464513-7de9-40a4-abd2-a78080af6d38

📥 Commits

Reviewing files that changed from the base of the PR and between fc70c0d and 843216d.

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

@Brazol Brazol merged commit ba0af7d into main May 29, 2026
10 of 15 checks passed
@Brazol Brazol deleted the fix/connection-flickering-rejoin branch May 29, 2026 07:15
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