perf: optimize backoff timing for 15s rate limit window

Changed INITIAL_BACKOFF_MS from 1s to 3s to reduce wasted retry attempts.

Before (1s base):
- Attempt 1: ~0.5-1s (total ~1s) - too early
- Attempt 2: ~1-2s (total ~2-3s) - still too early
- Attempt 3: ~2-4s (total ~4-7s) - still too early
- Attempt 4: ~4-8s (total ~8-15s) - maybe succeeds
- Attempt 5: ~8-16s (total ~16-31s) - succeeds

After (3s base):
- Attempt 1: ~2-3s (total ~3s) - strategic wait
- Attempt 2: ~4-6s (total ~7-9s) - approaching window
- Attempt 3: ~8-12s (total ~15-21s) - typically succeeds at ~15s
- Attempt 4: ~16-24s (total ~31-45s) - handles future longer limits
- Attempt 5: ~32s (total ~63-77s) - final safety net

Benefits:
- Reduces wasted attempts from 3-4 to 2-3 before success
- Aligns better with observed 15s rate limit recovery
- Still handles future rate limit increases (30s+) via attempts 4-5
- Maintains 5 max retries for flexibility
This commit is contained in:
Hossain Khan
2026-02-08 21:46:24 -05:00
parent 0a94856d82
commit 39a1ea313c
2 changed files with 23 additions and 6 deletions
@@ -24,10 +24,16 @@ import kotlin.random.Random
* - Logs retry attempts for debugging
* - Emits retry events for UI feedback
*
* Exponential backoff formula:
* - Base delay: 1 second
* - Delay = base * (2 ^ attempt) with jitter
* Exponential backoff formula (optimized for ~15s rate limit recovery):
* - Base delay: 3 seconds (optimized for typical API rate limits)
* - Delay = base * (2 ^ attempt-1) * jitter
* - Jitter: delay * (0.5 + 0.5 * random) to distribute load
* - Expected progression with jitter (for current ~15s rate limit):
* - Attempt 1: ~2-3s delay (total ~3s elapsed)
* - Attempt 2: ~4-6s delay (total ~7-9s elapsed)
* - Attempt 3: ~8-12s delay (total ~15-21s elapsed) - typically succeeds
* - Attempt 4: ~16-24s delay (total ~31-45s elapsed) - handles longer rate limits
* - Attempt 5: ~32s delay (total ~63-77s elapsed) - final safety net
* - Max delay: 32 seconds per retry (for exponential backoff only)
* - Retry-After header takes precedence and is NOT capped
*
@@ -72,9 +78,20 @@ class RateLimitInterceptor(
private const val MAX_RETRIES = 5
/**
* Initial backoff delay in milliseconds (1 second).
* Initial backoff delay in milliseconds (3 seconds).
*
* Optimized for typical API rate limit recovery time of ~15 seconds.
* With exponential backoff and jitter, this produces:
* - Attempt 1: ~2-3s delay (total ~3s elapsed)
* - Attempt 2: ~4-6s delay (total ~7-9s elapsed)
* - Attempt 3: ~8-12s delay (total ~15-21s elapsed) - typically succeeds at 15s
* - Attempt 4: ~16-24s delay (total ~31-45s elapsed) - handles longer rate limits
* - Attempt 5: ~32s delay (total ~63-77s elapsed) - final attempt
*
* This reduces wasted attempts compared to 1s initial backoff while still
* providing enough attempts to handle future longer rate limit windows.
*/
private const val INITIAL_BACKOFF_MS = 1000L
private const val INITIAL_BACKOFF_MS = 3000L
/**
* Maximum backoff delay in milliseconds (32 seconds).
@@ -585,7 +585,7 @@ fun PreviewTrmnlMirrorDisplayRetryingContent() {
TrmnlMirrorDisplayScreen.RetryInfo(
attempt = 2,
maxRetries = 5,
delaySeconds = 4,
delaySeconds = 6,
reason = "Rate limited",
),
eventSink = {},