Skip to content

Replace SfuConnectException with SfuConnectFailureCause code#1744

Merged
PratimMallick merged 3 commits into
fix/sfu-join-self-triggered-reconnectfrom
fix/rahullohra/sfu-join-self-triggered-reconnect
Jul 13, 2026
Merged

Replace SfuConnectException with SfuConnectFailureCause code#1744
PratimMallick merged 3 commits into
fix/sfu-join-self-triggered-reconnectfrom
fix/rahullohra/sfu-join-self-triggered-reconnect

Conversation

@rahul-lohra

@rahul-lohra rahul-lohra commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Goal

Refactor SFU connect failure classification to use domain-level failure causes instead of exception subclasses.
AN-1294
The implementation on PR: Recover initial join when connect safety-timeout leaves no reconnect
uses SfuConnectException.Timeout and SfuConnectException.Disconnected to tell _join() how to handle a failed SFU connect attempt. That works mechanically, but the model is misleading:

  • Timeout is not a network-layer exception here; it means connectInternal() gave up waiting for the socket to reach a terminal state.
  • Disconnected is an SFU socket state, not an exception.
  • _join() is really branching on domain failure causes, not exception types.

This PR is primarily a naming/modeling refactor over the existing recovery behavior, intended to make the SFU connection lifecycle easier to reason about and avoid treating socket states as exceptions.

Implementation

Replace SfuConnectException with:

internal sealed interface SfuConnectFailureCause {

     // RtcSession.connectInternal stopped waiting for the socket to reach a terminal state
    data object SocketStateObservationTimeout : SfuConnectFailureCause

     //The socket failure is already being recovered by RtcSession.stateJob.
    data object RecoverableSocketFailure : SfuConnectFailureCause

     // The socket failure should fail immediately without recovery.
    data object TerminalSocketFailure : SfuConnectFailureCause
}

Refactor SfuConnectionResult.Failure

Before

internal sealed class SfuConnectionResult {
    data class Failure(
        val recoverable: boolean = false,
         ....
    ) : SfuConnectionResult()
}

After

internal sealed class SfuConnectionResult {
    data class Failure(
        val cause: SfuConnectFailureCause,
         ....
    ) : SfuConnectionResult()
}

🎨 UI Changes

None

Testing

Not required, this is a code refactor on WIP PR

Summary by CodeRabbit

  • Bug Fixes
    • Improved SFU connection recovery during call joining.
    • Recoverable connection failures now wait for ongoing recovery, while timeouts trigger a rejoin attempt.
    • Terminal connection failures now fail immediately with appropriate analytics reporting.
    • Improved handling and classification of connection timeouts and socket disconnects.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@github-actions

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 12.27 MB 12.27 MB 0.00 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.68 MB 0.00 MB 🟢
stream-video-android-ui-compose 6.20 MB 6.20 MB 0.00 MB 🟢

@rahul-lohra rahul-lohra changed the title fix: Refactor with SfuConnectFailureCause code Replace SfuConnectException with SfuConnectFailureCause code Jul 10, 2026
@rahul-lohra rahul-lohra marked this pull request as ready for review July 11, 2026 05:54
@rahul-lohra rahul-lohra requested a review from a team as a code owner July 11, 2026 05:54
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

SFU connection failures now carry typed causes for observation timeouts, recoverable socket failures, and terminal socket failures. RtcSession.connectInternal classifies outcomes, while Call._join selects REJOIN, recovery waiting, or immediate failure behavior. Tests validate the updated result model and flows.

Changes

SFU failure classification and recovery

Layer / File(s) Summary
Connection failure contract and classification
stream-video-android-core/src/main/kotlin/.../call/{RtcSession.kt,SfuConnectFailureCause.kt}
Introduces typed failure causes and maps connection timeouts and socket disconnects to them.
Join recovery orchestration
stream-video-android-core/src/main/kotlin/.../{Call.kt,call/RtcSession.kt}
Updates _join to trigger REJOIN, await recovery, or fail immediately based on the typed cause.
Failure behavior tests
stream-video-android-core/src/test/kotlin/.../rtc/{FastReconnectIceRestartTest.kt,JoinRecoverableFailureTest.kt,RtcSessionTest2.kt}
Updates stubs, assertions, and test descriptions for typed failure causes and recovery behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Call._join
  participant RtcSession.connectInternal
  participant reconnect
  Call._join->>RtcSession.connectInternal: connect to SFU
  RtcSession.connectInternal-->>Call._join: SfuConnectionResult.Failure(cause)
  Call._join->>reconnect: start REJOIN for observation timeout
  reconnect-->>Call._join: recovery outcome
  Call._join-->>Call._join: fail immediately for terminal failure
Loading

Possibly related PRs

Suggested labels: pr:improvement

Suggested reviewers: PratimMallick, aleksandar-apostolov

Poem

I’m a rabbit with causes, three neat in a row,
Timeout says “REJOIN!” and recoveries flow.
Terminal thumps shut with a failure so clear,
Tests guard the burrow from bug-shaped fear.
Hop, hop—typed errors are here!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly states the main refactor from SfuConnectException to SfuConnectFailureCause.
Description check ✅ Passed It covers Goal, Implementation, UI Changes, and Testing; the missing checklist and GIF sections are non-critical.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rahullohra/sfu-join-self-triggered-reconnect

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.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
44.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@PratimMallick PratimMallick merged commit f9369e3 into fix/sfu-join-self-triggered-reconnect Jul 13, 2026
12 of 13 checks passed
@PratimMallick PratimMallick deleted the fix/rahullohra/sfu-join-self-triggered-reconnect branch July 13, 2026 11:51
PratimMallick added a commit that referenced this pull request Jul 13, 2026
…1741)

* fix(core): recover initial join when connect safety-timeout leaves no reconnect

When RtcSession.connectInternal hits its own safety-timeout, the socket is
left in a non-terminal state that stateJob ignores, so no Call.reconnect is
ever launched and _join's didReconnectSucceed() would block forever.

- Add SfuConnectionResult.Failure.reconnectTriggered so the join flow can tell
  whether a recovery loop has already been started by stateJob.
- On the safety-timeout path, tear down the abandoned (still-in-flight) socket
  so a late Connected/JoinResponse can't resurface and resurrect the session.
- In Call._join, trigger a REJOIN ourselves when a recoverable failure reports
  no reconnect was triggered; otherwise await the existing loop.
- Rename canTriggersReconnect() to triggersStateJobReconnect() for clarity.
- demo-app: align connectionTimeoutInMs with the SDK default (5s).

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(core): type SFU connect failures instead of a reconnect flag

Address PR review: reconnect-orchestration state should not live on the
SfuConnectionResult.Failure DTO. Replace the boolean flag with a typed error
so callers of connectInternal branch on the failure kind.

- Add sealed SfuConnectException with Timeout (connect safety-timeout tore down
  a stuck socket; no recovery started) and Disconnected (terminal SFU state).
- connectInternal now returns these typed errors instead of Exception("msg").
- Drop SfuConnectionResult.Failure.hasReconnectStarted; _join self-triggers a
  REJOIN only when the error is SfuConnectException.Timeout, else awaits the
  loop stateJob already started.
- Update tests to construct/assert the typed errors.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Replace SfuConnectException with SfuConnectFailureCause code (#1744)

* fix: Refactor with SfuConnectFailureCause code

* fix: Add kdoc

* fix: Replace Singletone shared Call.testInstanceProvider.rtcSessionCreator with  Call..unitTestRtcSessionFactory

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Aleksandar Apostolov <apostolov.alexandar@gmail.com>
Co-authored-by: Rahul Kumar Lohra <tgunix@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Enhances an existing feature or code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants