From 9f8361930df963ddee5be027ad0104b84055af3c Mon Sep 17 00:00:00 2001 From: Alisher Date: Wed, 3 Jun 2026 08:09:40 +0200 Subject: [PATCH] fix(filter): suppress health-check pings in background mode FilterHealthCheckLoop fires every 1 min and sends a Waku protocol ping to each subscribed filter peer. On Android, these pings queue up during Doze and flush in maintenance windows (~15 min), keeping the LTE modem at ~55-58% duty cycle even when the app is backgrounded. This was the dominant radio wakeup source missed by the previous commit (which only gated subscription renewal in subscriptionLoop). mag's measurement on #21111 confirmed modem still 58% active post-R1. Add backgroundMode atomic.Bool to WakuFilterLightNode with a SetBackgroundMode(bool) method. FilterHealthCheckLoop skips PingPeers() when backgrounded. FilterManager.SetBackgroundMode now cascades to both layers: subscription renewal (api/filter) and health-check pings (protocol/filter). Co-Authored-By: Claude Sonnet 4.6 --- waku/v2/api/filter/filter_manager.go | 3 +++ waku/v2/protocol/filter/client.go | 2 ++ waku/v2/protocol/filter/filter_health_check.go | 12 ++++++++++++ 3 files changed, 17 insertions(+) diff --git a/waku/v2/api/filter/filter_manager.go b/waku/v2/api/filter/filter_manager.go index 5f2c2589..0388fbc4 100644 --- a/waku/v2/api/filter/filter_manager.go +++ b/waku/v2/api/filter/filter_manager.go @@ -197,9 +197,12 @@ func (mgr *FilterManager) NetworkChange() { func (mgr *FilterManager) SetBackgroundMode(background bool) { mgr.Lock() defer mgr.Unlock() + // Gate subscription renewal (api/filter layer) for _, subDetails := range mgr.filterSubscriptions { subDetails.sub.SetBackgroundMode(background) } + // Gate health-check pings (protocol/filter layer) — the dominant LTE radio wakeup source + mgr.node.SetBackgroundMode(background) } // checkAndProcessQueue drains the offline-pending filter queue. For each batch diff --git a/waku/v2/protocol/filter/client.go b/waku/v2/protocol/filter/client.go index 727200ba..bd6dccb9 100644 --- a/waku/v2/protocol/filter/client.go +++ b/waku/v2/protocol/filter/client.go @@ -8,6 +8,7 @@ import ( "math" "net/http" "sync" + "sync/atomic" "time" "github.com/libp2p/go-libp2p/core/host" @@ -57,6 +58,7 @@ type WakuFilterLightNode struct { pm *peermanager.PeerManager limiter *utils.RateLimiter peerPingInterval time.Duration + backgroundMode atomic.Bool // true when app UI is not visible; suppresses health-check pings } type WakuFilterPushError struct { diff --git a/waku/v2/protocol/filter/filter_health_check.go b/waku/v2/protocol/filter/filter_health_check.go index 86fa77e7..78ec7d3d 100644 --- a/waku/v2/protocol/filter/filter_health_check.go +++ b/waku/v2/protocol/filter/filter_health_check.go @@ -42,6 +42,13 @@ func (wf *WakuFilterLightNode) PingPeer(peer peer.ID) { } } +// SetBackgroundMode suppresses (background=true) or re-enables (background=false) +// the periodic health-check pings sent to filter peers. Call with background=true +// when the app UI is not visible to avoid waking the LTE radio during Doze windows. +func (wf *WakuFilterLightNode) SetBackgroundMode(background bool) { + wf.backgroundMode.Store(background) +} + func (wf *WakuFilterLightNode) FilterHealthCheckLoop() { defer utils.LogOnPanic() defer wf.WaitGroup().Done() @@ -50,6 +57,11 @@ func (wf *WakuFilterLightNode) FilterHealthCheckLoop() { for { select { case <-ticker.C: + if wf.backgroundMode.Load() { + // In background: skip health-check ping to avoid waking the LTE radio. + // SetBackgroundMode(false) will resume pings on foreground return. + continue + } if wf.onlineChecker.IsOnline() { wf.PingPeers() }