From 39a1ea313c2041578fc063416b1ffd4f64ca5487 Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Sun, 8 Feb 2026 21:46:24 -0500 Subject: [PATCH] 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 --- .../android/network/RateLimitInterceptor.kt | 27 +++++++++++++++---- .../ui/display/TrmnlMirrorDisplayScreen.kt | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/ink/trmnl/android/network/RateLimitInterceptor.kt b/app/src/main/java/ink/trmnl/android/network/RateLimitInterceptor.kt index de1c805..d31d6cb 100644 --- a/app/src/main/java/ink/trmnl/android/network/RateLimitInterceptor.kt +++ b/app/src/main/java/ink/trmnl/android/network/RateLimitInterceptor.kt @@ -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). diff --git a/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt b/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt index 177204e..0e5e000 100644 --- a/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt @@ -585,7 +585,7 @@ fun PreviewTrmnlMirrorDisplayRetryingContent() { TrmnlMirrorDisplayScreen.RetryInfo( attempt = 2, maxRetries = 5, - delaySeconds = 4, + delaySeconds = 6, reason = "Rate limited", ), eventSink = {},