Skip to content

Add proportional jitter to the default reconnect delays - #1045

Open
xianshijing-lk wants to merge 1 commit into
mainfrom
port/js-2124-reconnect-jitter
Open

xianshijing-lk wants to merge 1 commit into
mainfrom
port/js-2124-reconnect-jitter

Conversation

@xianshijing-lk

Copy link
Copy Markdown
Contributor

Port of livekit/client-sdk-js#2124.

The bug

DefaultReconnectPolicy.getNextRetryDelay returned retryDelays[context.retryCount] verbatim — no jitter at any attempt.

Every client disconnected by the same event (a server restart, a region failover, a dropped uplink) therefore waits exactly 100ms, then 300ms, then 300ms, in lockstep. They retry as one herd against the server that just came back. Worse, because the schedule is fully deterministic, clients do not drift apart over successive attempts the way they would with any jitter at all — they stay synchronised for the whole backoff.

This is the same defect the JS SDK fixed, and a bit more pronounced here: the pre-fix JS code at least jittered attempts after the second.

Repro

Added DefaultReconnectPolicyTest. Against the unfixed code:

DefaultReconnectPolicyTest > spreads out clients that disconnect at the same moment FAILED
DefaultReconnectPolicyTest > gives a zero first delay a bounded random window FAILED

100 callers asking for their first retry delay all received the identical value.

The fix

Scale each delay by a random factor in [0.5, 1.5), so the spread widens as the base delay grows. A first retry whose configured delay is zero has nothing to scale, so it draws from a fixed 0–500ms window instead; a zero at any later index is an explicit request to retry immediately and still passes through as zero.

The remaining five tests in the new file cover the behaviour that must not change: exhausting the delay list, exceeding maxReconnectionTimeout, custom list lengths, and the later-zero passthrough.

Note on behaviour

DefaultReconnectPolicy is public API and callers can supply their own retryDelays. Those custom delays are now jittered too, which is the intent — the herd problem is independent of who picked the numbers — but it does mean a caller who previously relied on an exact delay will see a range. Flagging in case you'd rather gate it.

Verification

:livekit-android-test:testReleaseUnitTest — the two assertions above fail before the change and pass after; full module suite green.

Scope

The upstream PR also changed RTCEngine to route a server-requested reconnect through the policy instead of hardcoding delay = 0. This SDK has no such shortcut — RTCEngine.kt already takes its delay solely from the policy — so only the policy half applies here.

🤖 Generated with Claude Code

DefaultReconnectPolicy returned a fixed delay per retry count, so every
client disconnected by the same event waited exactly 100ms, then 300ms,
then 300ms, in lockstep. They retried as one herd, and because the
schedule is identical they could stay synchronised across attempts
rather than drifting apart.

Scale each delay by a random factor in [0.5, 1.5) so clients spread
further apart with every retry. A first retry configured as zero draws
from a fixed 0-500ms window instead, since there is no base delay to
scale; a zero at any later index still means retry immediately.

Port of livekit/client-sdk-js#2124.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1a36ff7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
client-sdk-android Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@github-actions

Copy link
Copy Markdown
Contributor

Diffuse output:

OLD: diffuse-source-file
NEW: livekit-android-sdk-release.aar

 AAR      │ old      │ new      │ diff   
──────────┼──────────┼──────────┼────────
      jar │    3 MiB │    3 MiB │ +344 B 
 manifest │  1.5 KiB │  1.5 KiB │    0 B 
 lint-jar │ 12.7 KiB │ 12.7 KiB │    0 B 
    other │  2.4 KiB │  2.4 KiB │    0 B 
──────────┼──────────┼──────────┼────────
    total │    3 MiB │    3 MiB │ +344 B 

 JAR     │ old   │ new   │ diff       
─────────┼───────┼───────┼────────────
 classes │  1707 │  1707 │  0 (+0 -0) 
 methods │ 21992 │ 21997 │ +5 (+5 -0) 
  fields │  5708 │  5710 │ +2 (+2 -0)
AAR
 size  │ diff   │ path          
───────┼────────┼───────────────
 3 MiB │ +344 B │ ∆ classes.jar 
───────┼────────┼───────────────
 3 MiB │ +344 B │ (total)
JAR
METHODS:

   old   │ new   │ diff       
  ───────┼───────┼────────────
   21992 │ 21997 │ +5 (+5 -0) 
  
  + kotlin.random.Random_Default nextDouble() → double
  + kotlin.random.Random_Default nextDouble(double) → double
  + kotlin.time.Duration times-UwyO8pc(long, double) → long
  + kotlin.time.Duration_Companion getZERO-UwyO8pc() → long
  + kotlin.time.DurationKt toDuration(double, DurationUnit) → long
  

FIELDS:

   old  │ new  │ diff       
  ──────┼──────┼────────────
   5708 │ 5710 │ +2 (+2 -0) 
  
  + io.livekit.android.room.network.DefaultReconnectPolicy MAX_FIRST_RETRY_JITTER_IN_MS: double
  + kotlin.random.Random Default: Random_Default

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.

1 participant