From 821ec7c5f792269f8a9328833e558d65427f285c Mon Sep 17 00:00:00 2001 From: Moe Hamade <69801237+moehamade@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:07:59 +0300 Subject: [PATCH] fix: check the cooldown on every attempt, not once before the loop Addresses review on #812. Each retry resampled the route but never rechecked the gate against it, so a route change during a request or its backoff walked straight past a cooldown. A Tor attempt failing with a 500, then the user switching to a direct connection that is already rate limited, and the next attempt contacts it regardless. The check moves inside the loop, immediately after the route is sampled, which makes it cover the first attempt too -- the separate post-wait check it replaces was only ever that first iteration. The check before the route wait stays: knowing the selected route is blocked is worth avoiding a sixty-second Tor bootstrap for. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/bitchat/android/util/GitHubReleaseClient.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/util/GitHubReleaseClient.kt b/app/src/main/java/com/bitchat/android/util/GitHubReleaseClient.kt index 8206d182..a5acefe2 100644 --- a/app/src/main/java/com/bitchat/android/util/GitHubReleaseClient.kt +++ b/app/src/main/java/com/bitchat/android/util/GitHubReleaseClient.kt @@ -185,12 +185,6 @@ object GitHubReleaseClient { // for the whole metadata request. onResolvingRelease?.invoke() - // That wait can last a minute, in which time the user may have switched - // routes. The cooldown that matters is the one for the route the request - // will actually take, which is only known now. - blockedResultOrNull(System.currentTimeMillis(), selectedRouteUsesTor(), cached) - ?.let { return@withLock it } - var lastFailure: Throwable = ReleaseFetchException( "Failed to fetch the latest release from GitHub" ) @@ -201,6 +195,12 @@ object GitHubReleaseClient { // the request did not use. val routeUsesTor = selectedRouteUsesTor() + // Per attempt rather than once before the loop. The route can change + // during the wait above, during a request, or during a backoff, and + // the one we have just switched to may carry a cooldown of its own. + blockedResultOrNull(System.currentTimeMillis(), routeUsesTor, cached) + ?.let { return@withLock it } + val result = fetchLatestReleaseOnce(routeUsesTor) result.onSuccess { release -> cachedRelease = CachedRelease(release, System.currentTimeMillis())