From dbfd2c4c43a1e26baf23b76c87860e6e971252f2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Jun 2026 17:53:39 +0000 Subject: [PATCH] fix(notif): throttle relay-count updates to dodge Android's rate limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A device log showed the persistent notification stuck on a stale count (e.g. "44 inbox relays") while the pool had actually settled lower (flowConnected=8). Cause: the count collector posted the notification on every connectedRelaysFlow delta — ~90 updates during feed load, then ~22 in ~250ms during background teardown. Android rate-limits notification updates (~10/s) and silently drops the excess, so the last value the framework rendered (a mid-cascade 44) stuck instead of the final 8. Sample connectedRelaysFlow at 1s before updating the notification. That caps updates to ~1/s — comfortably under the limit — and the settled count always lands. Also drops the now-confirmed notif-collector/ notif-popup debug logging. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Ukw6FJPFh3JKGXL532p3ae --- .../notifications/NotificationRelayService.kt | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt index 02d1a1b4f4..36acb126e2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt @@ -43,10 +43,12 @@ import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch /** @@ -80,6 +82,10 @@ class NotificationRelayService : Service() { private const val ACTION_START = "com.vitorpamplona.amethyst.START_NOTIFICATION_SERVICE" + // Throttle interval for refreshing the persistent notification's relay count. + // Keeps notification updates well under Android's rate limit (~10/s). + private const val NOTIFICATION_REFRESH_MS = 1000L + const val ACTION_AUTO_RESTART = "com.vitorpamplona.amethyst.AUTO_RESTART_NOTIFICATION_SERVICE" fun start(context: Context) { @@ -243,6 +249,7 @@ class NotificationRelayService : Service() { * drafts, and relay list changes. Since the service keeps the client connected, * those subscriptions remain active on the relays. */ + @OptIn(FlowPreview::class) private fun startRelayConnection() { relayServiceCollectorJob?.cancel() relayServiceCollectorJob = @@ -254,15 +261,22 @@ class NotificationRelayService : Service() { } launch { - Amethyst.instance.client.connectedRelaysFlow().collectLatest { relays -> - val count = relays.size - Log.d("BgRelayTrace") { "notif-collector received connectedRelays=$count (lastPosted=$connectedRelayCount)" } - if (count != connectedRelayCount) { - connectedRelayCount = count - updateNotification(count) - Log.d("BgRelayTrace") { "notif-popup posted count=$count" } + // sample() caps how often we touch the notification. During feed + // load/teardown connectedRelaysFlow churns dozens of times per second; + // posting on every delta blows past Android's notification rate limit + // (~10/s), which silently drops updates and leaves the visible count + // stuck on a stale intermediate value. One refresh per second stays + // well under the limit and always lands the settled count. + Amethyst.instance.client + .connectedRelaysFlow() + .sample(NOTIFICATION_REFRESH_MS) + .collectLatest { relays -> + val count = relays.size + if (count != connectedRelayCount) { + connectedRelayCount = count + updateNotification(count) + } } - } } } }