mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-09-14 00:35:08 +00:00
Debounce relay stat counter updates to avoid notification rate limit
AmberRelayStats.addSent/addFailed called notify(id=2) synchronously for every relay event, driven by NostrClientLoggerListener.onSent. Bursts of relay traffic (7-34 notifies/sec vs Android's ~5/sec per-app limit) made NotificationManagerService shed the updates, freezing the status notification on stale text, and wasted a full BigTextStyle build plus Binder IPC per dropped event. Route the counter path through a Unit event flow (MutableSharedFlow, capacity 1, DROP_OLDEST) collected with debounce(300): a burst collapses into one notification update and the last tick always renders. No numeric counter, so nothing overflows or wraps no matter how long the process runs; tryEmit never suspends or fails on Quartz IO threads. Connection-state updates keep their existing debounce so connect/disconnect still shows promptly. Fixes ngit issue nevent1qy28wumn8ghj7un9d3shjtnwva5hgtnyv4mqqg80gw6ushd8jyj6rt8qseup7hngj0xhqa2ms3dvam60lhc760zvkyq6uts9
This commit is contained in:
@@ -25,6 +25,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -39,6 +41,23 @@ class AmberRelayStats(
|
||||
var available = emptySet<NormalizedRelayUrl>()
|
||||
var connected = emptySet<NormalizedRelayUrl>()
|
||||
|
||||
// Counter updates arrive per relay event (NostrClientLoggerListener.onSent),
|
||||
// which can burst at 30+ events/sec and trip Android's per-app notification
|
||||
// rate limit (~5/sec): NotificationManager sheds every update and the status
|
||||
// notification freezes on stale text until the burst ends. Emitting a tick
|
||||
// defers notify() to the debounced collector started in
|
||||
// createNotificationChannel(), so a burst collapses into one notification
|
||||
// update. Connection-state changes keep their own (also debounced)
|
||||
// collectors so connect/disconnect still shows promptly.
|
||||
// Unit events instead of a numeric counter: nothing to overflow or wrap no
|
||||
// matter how long the app runs. DROP_OLDEST + capacity 1 keeps only the
|
||||
// newest tick, so tryEmit never fails and the last event of a burst always
|
||||
// renders.
|
||||
private val counterTick = MutableSharedFlow<Unit>(
|
||||
extraBufferCapacity = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
@SuppressLint("MissingPermission")
|
||||
val relayStatus = combine(client.availableRelaysFlow(), client.connectedRelaysFlow()) { available, connected ->
|
||||
@@ -107,6 +126,14 @@ class AmberRelayStats(
|
||||
updateNotification()
|
||||
}
|
||||
}
|
||||
Amber.instance.applicationIOScope.launch {
|
||||
// Trailing-edge debounce for the counter path (addSent/addFailed):
|
||||
// the last tick of a burst always renders, at most one notify per
|
||||
// 300ms window.
|
||||
counterTick.debounce(300).collect {
|
||||
updateNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val innerCache = mutableMapOf<NormalizedRelayUrl, AmberRelayStat>()
|
||||
@@ -253,12 +280,16 @@ class AmberRelayStats(
|
||||
|
||||
fun addSent(url: NormalizedRelayUrl) {
|
||||
get(url).addSent()
|
||||
updateNotification()
|
||||
scheduleNotificationUpdate()
|
||||
}
|
||||
|
||||
fun addFailed(url: NormalizedRelayUrl) {
|
||||
get(url).addFailed()
|
||||
updateNotification()
|
||||
scheduleNotificationUpdate()
|
||||
}
|
||||
|
||||
private fun scheduleNotificationUpdate() {
|
||||
counterTick.tryEmit(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user