mirror of
https://github.com/logos-messaging/logos-delivery-go.git
synced 2026-06-27 16:49:26 +00:00
fix(filter): suppress subscription renewal in background mode
Adds backgroundMode to Sub and SetBackgroundMode(bool) to both Sub and FilterManager. When background=true, subscriptionLoop skips the 5-second health-check ticker and drops expired subscription IDs from the closing channel without resubscribing. When background=false (foreground return), a resubscription is immediately triggered for any expired filters. Background context (status-im/status-app#21045): On Android, each Waku filter subscription has a relay-side TTL (~13.5 min observed). When it expires, the closing channel fires, checkAndResubscribe runs, and a new wf.Subscribe() RPC wakes the LTE modem. With a loaded account this happens every ~13.5 min overnight, producing a 55% radio duty cycle (~144 mAh/hr) while the screen is locked. With background mode active, no network I/O occurs during subscription expiry. On foreground return, all expired filters are resubscribed in one burst — the user sees a brief reconnect, then full message delivery. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d6d39395ac
commit
ab4609ef15
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -57,6 +58,9 @@ type Sub struct {
|
||||
resubscribeInProgress bool
|
||||
id string
|
||||
errcnt int
|
||||
// backgroundMode suppresses subscription renewal when the app UI is not visible.
|
||||
// Toggled via SetBackgroundMode; read from subscriptionLoop goroutine.
|
||||
backgroundMode atomic.Bool
|
||||
// rateLimitedUntil is set when subscribe() observes a *SubscribeError whose
|
||||
// FailedPeers contain at least one HTTP 429. While time.Now().Before(rateLimitedUntil),
|
||||
// subscriptionLoop suppresses retry triggers (ticker push and checkAndResubscribe).
|
||||
@ -139,6 +143,11 @@ func (apiSub *Sub) subscriptionLoop(loopInterval time.Duration) {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
apiSub.errcnt = 0 //reset errorCount
|
||||
if apiSub.backgroundMode.Load() {
|
||||
// In background: skip health check to avoid waking the LTE radio.
|
||||
// SetBackgroundMode(false) triggers resubscription on foreground.
|
||||
continue
|
||||
}
|
||||
if shouldHonourRateLimitBackoff(apiSub.rateLimitedUntil, time.Now()) {
|
||||
apiSub.log.Debug("ticker push suppressed by rate-limit backoff",
|
||||
zap.Time("rate-limited-until", apiSub.rateLimitedUntil),
|
||||
@ -154,6 +163,14 @@ func (apiSub *Sub) subscriptionLoop(loopInterval time.Duration) {
|
||||
apiSub.cleanup()
|
||||
return
|
||||
case subId := <-apiSub.closing:
|
||||
if apiSub.backgroundMode.Load() {
|
||||
// In background: subscription expired but don't resubscribe now.
|
||||
// SetBackgroundMode(false) will trigger resubscription on foreground.
|
||||
apiSub.log.Debug("resubscribe suppressed: app in background",
|
||||
zap.String("sub-id", subId),
|
||||
)
|
||||
continue
|
||||
}
|
||||
if shouldHonourRateLimitBackoff(apiSub.rateLimitedUntil, time.Now()) {
|
||||
apiSub.log.Debug("checkAndResubscribe suppressed by rate-limit backoff",
|
||||
zap.Time("rate-limited-until", apiSub.rateLimitedUntil),
|
||||
@ -174,6 +191,23 @@ func (apiSub *Sub) subscriptionLoop(loopInterval time.Duration) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetBackgroundMode controls whether this subscription suppresses renewal.
|
||||
// Call with background=true when the app UI is not visible (screen locked).
|
||||
// Call with background=false when returning to foreground; this triggers an
|
||||
// immediate resubscription attempt for any subscriptions that expired while
|
||||
// backgrounded.
|
||||
func (apiSub *Sub) SetBackgroundMode(background bool) {
|
||||
apiSub.backgroundMode.Store(background)
|
||||
if !background {
|
||||
// Returning to foreground: prod the loop to resubscribe.
|
||||
select {
|
||||
case apiSub.closing <- "":
|
||||
default:
|
||||
// A resubscription is already queued.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (apiSub *Sub) checkAndResubscribe(subId string) {
|
||||
var failedPeer peer.ID
|
||||
if subId != "" {
|
||||
|
||||
@ -47,9 +47,9 @@ type FilterManager struct {
|
||||
// a deadlock where SubscribeFilter would block on a full channel while still
|
||||
// holding mgr.Lock(), preventing the only drainer (checkAndProcessQueue, also
|
||||
// invoked under the same lock) from running.
|
||||
waitingToSubQueue []filterConfig
|
||||
envProcessor EnevelopeProcessor
|
||||
networkConnType byte
|
||||
waitingToSubQueue []filterConfig
|
||||
envProcessor EnevelopeProcessor
|
||||
networkConnType byte
|
||||
}
|
||||
|
||||
type SubDetails struct {
|
||||
@ -189,6 +189,19 @@ func (mgr *FilterManager) NetworkChange() {
|
||||
mgr.node.PingPeers() // ping all peers to check if subscriptions are alive
|
||||
}
|
||||
|
||||
// SetBackgroundMode notifies all active subscriptions of the app's visibility
|
||||
// state. When background=true, subscriptions suppress renewal keepalives to
|
||||
// avoid waking the LTE radio while the screen is locked. When background=false
|
||||
// (returning to foreground), each subscription immediately attempts to
|
||||
// resubscribe if its filter has expired.
|
||||
func (mgr *FilterManager) SetBackgroundMode(background bool) {
|
||||
mgr.Lock()
|
||||
defer mgr.Unlock()
|
||||
for _, subDetails := range mgr.filterSubscriptions {
|
||||
subDetails.sub.SetBackgroundMode(background)
|
||||
}
|
||||
}
|
||||
|
||||
// checkAndProcessQueue drains the offline-pending filter queue. For each batch
|
||||
// that matches the given pubsubTopic (or always, when pubsubTopic == ""), a
|
||||
// subscribe goroutine is spawned; non-matching batches are retained for a
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user