Skip to content

feat: pause background work when app is in background#7373

Open
alexjba wants to merge 6 commits intodevelopfrom
feat/reduce-app-work
Open

feat: pause background work when app is in background#7373
alexjba wants to merge 6 commits intodevelopfrom
feat/reduce-app-work

Conversation

@alexjba
Copy link
Contributor

@alexjba alexjba commented Mar 16, 2026

StatusApp PR status-im/status-app#20202

Iterates status-im/status-app#20227

Introduce a lifecycle-based pause/resume mechanism to reduce non-essential
work when the app goes to background, improving battery and resource usage. This is needed to reduce the battery impact when status-go runs as a background service.

  • Add pkg/messaging/lifecycle for SubscribePausedBackground/SetPausedBackground
  • Add backend pause/play (pauseLocked/resumeLocked) driven by AppStateChange
  • StatusNode.PauseBackground/ResumeForeground coordinate services via
    backgroundLifecycle interface
  • Messenger.ToForeground/ToBackground broadcast to lifecycle, datasync,
    and push notification client
  • Pause tickers/loops in: centralizedmetrics, ipfs, rpc limiter, ntp,
    backup, transport, envelopes monitor, connector, wallet, newsfeed, ext
  • Simplify mobile AppStateChange (remove AppStateChangeV2, single API)
  • RPC client: clean up limiters on Stop

Other changes:

  • Update local notifications to show the received message where possible
  • Reuse the same media server port after server restart

@github-actions
Copy link

github-actions bot commented Mar 16, 2026

⚠️ Companion PR Needs Update

#20202 is not using the latest status-go commit (a1ecaed276146caca2b4983f023fd59e4fac9197).

Update vendor/status-go in the companion PR.

@alexjba alexjba changed the title Feat/reduce app work feat: pause background work when app is in background Mar 16, 2026
@status-im-auto
Copy link
Member

status-im-auto commented Mar 16, 2026

Jenkins Builds

Click to see older builds (102)
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ 5ca6800 1 2026-03-16 08:59:22 ~4 min linux/status-go 📦zip
✔️ 5ca6800 1 2026-03-16 08:59:51 ~4 min macos/status-go 📦zip
✖️ 5ca6800 1 2026-03-16 09:00:47 ~5 min tests 📄log
✖️ 5ca6800 1 2026-03-16 09:02:57 ~7 min tests 📄log
✔️ 5ca6800 1 2026-03-16 09:03:49 ~8 min macos/nwaku 📦zip
✔️ 5ca6800 1 2026-03-16 09:04:02 ~8 min linux/nwaku 📦zip
✔️ 5ca6800 1 2026-03-16 09:06:19 ~10 min windows/status-go 📦zip
✔️ 5ca6800 1 2026-03-16 09:11:11 ~15 min tests-rpc 📄log
✔️ 5ca6800 1 2026-03-16 09:14:55 ~19 min tests-rpc 📄log
✔️ 5ca6800 1 2026-03-16 09:15:19 ~19 min windows/nwaku 📦zip
✔️ 03e7404 2 2026-03-16 11:26:17 ~8 min linux/status-go 📦zip
✔️ 03e7404 2 2026-03-16 11:29:15 ~10 min macos/status-go 📦zip
✔️ 03e7404 2 2026-03-16 11:29:22 ~10 min windows/status-go 📦zip
✖️ 03e7404 2 2026-03-16 11:29:49 ~11 min tests 📄log
✔️ 03e7404 2 2026-03-16 11:30:52 ~12 min windows/nwaku 📦zip
✔️ 03e7404 2 2026-03-16 11:31:03 ~12 min macos/nwaku 📦zip
✖️ 03e7404 2 2026-03-16 11:32:50 ~14 min tests 📄log
✔️ 03e7404 2 2026-03-16 11:32:52 ~14 min linux/nwaku 📦zip
✔️ 03e7404 2 2026-03-16 11:41:06 ~22 min tests-rpc 📄log
✖️ 03e7404 2 2026-03-16 11:53:08 ~34 min tests-rpc 📄log
✖️ 77a9572 3 2026-03-16 13:55:28 ~2 min tests 📄log
✔️ 77a9572 3 2026-03-16 13:57:18 ~4 min linux/status-go 📦zip
✔️ 77a9572 3 2026-03-16 13:57:31 ~4 min macos/status-go 📦zip
✖️ 77a9572 3 2026-03-16 13:57:36 ~4 min tests 📄log
✔️ 77a9572 3 2026-03-16 14:00:53 ~8 min linux/nwaku 📦zip
✔️ 77a9572 3 2026-03-16 14:03:17 ~10 min windows/status-go 📦zip
✔️ 77a9572 3 2026-03-16 14:03:32 ~10 min macos/nwaku 📦zip
✔️ 77a9572 3 2026-03-16 14:05:14 ~12 min windows/nwaku 📦zip
✔️ 77a9572 3 2026-03-16 14:07:57 ~15 min tests-rpc 📄log
✔️ 77a9572 3 2026-03-16 14:08:16 ~15 min tests-rpc 📄log
✖️ 830ab56 4 2026-03-16 15:02:29 ~3 min tests 📄log
✔️ 830ab56 4 2026-03-16 15:03:07 ~3 min linux/status-go 📦zip
✔️ 830ab56 4 2026-03-16 15:03:34 ~4 min macos/status-go 📦zip
✔️ 830ab56 4 2026-03-16 15:07:55 ~8 min linux/nwaku 📦zip
✔️ 830ab56 4 2026-03-16 15:09:45 ~10 min macos/nwaku 📦zip
✔️ 830ab56 4 2026-03-16 15:10:06 ~10 min windows/status-go 📦zip
✔️ 830ab56 4 2026-03-16 15:13:20 ~13 min windows/nwaku 📦zip
✔️ 830ab56 4 2026-03-16 15:13:50 ~14 min tests-rpc 📄log
✔️ 830ab56 4 2026-03-16 15:13:57 ~14 min tests-rpc 📄log
✔️ 830ab56 4 2026-03-16 15:35:22 ~35 min tests 📄log
✔️ 2715c40 5 2026-03-19 08:54:20 ~4 min macos/status-go 📦zip
✔️ 2715c40 5 2026-03-19 09:00:04 ~9 min windows/status-go 📦zip
✔️ 2715c40 5 2026-03-19 09:02:12 ~12 min macos/nwaku 📦zip
✔️ 2715c40 5 2026-03-19 09:08:28 ~18 min windows/nwaku 📦zip
✔️ 2715c40 5 2026-03-19 09:14:16 ~24 min linux/status-go 📦zip
✖️ 2715c40 5 2026-03-19 09:14:55 ~24 min tests 📄log
✔️ 2715c40 5 2026-03-19 09:19:49 ~30 min linux/nwaku 📦zip
✔️ 2715c40 5 2026-03-19 09:28:35 ~38 min tests-rpc 📄log
✔️ 2715c40 5 2026-03-19 09:28:36 ~38 min tests-rpc 📄log
✔️ 2715c40 5 2026-03-19 09:48:36 ~58 min tests 📄log
✖️ 2715c40 6 2026-03-20 12:45:53 ~2 min tests 📄log
✔️ 2715c40 6 2026-03-20 12:47:31 ~4 min macos/status-go 📦zip
✔️ 2715c40 6 2026-03-20 12:47:39 ~4 min linux/status-go 📦zip
✔️ 2715c40 6 2026-03-20 12:51:54 ~8 min linux/nwaku 📦zip
✔️ 2715c40 6 2026-03-20 12:53:03 ~9 min windows/status-go 📦zip
✔️ 2715c40 6 2026-03-20 12:58:50 ~15 min macos/nwaku 📦zip
✔️ 2715c40 6 2026-03-20 13:00:46 ~17 min tests-rpc 📄log
✔️ 2715c40 6 2026-03-20 13:04:07 ~20 min windows/nwaku 📦zip
✖️ 2715c40 6 2026-03-20 13:05:03 ~21 min tests-rpc 📄log
✔️ 2715c40 6 2026-03-20 13:22:21 ~38 min tests 📄log
✖️ bb149ec 7 2026-03-20 14:08:17 ~3 min tests 📄log
✔️ bb149ec 7 2026-03-20 14:09:07 ~4 min linux/status-go 📦zip
✔️ bb149ec 7 2026-03-20 14:09:27 ~4 min macos/status-go 📦zip
✔️ bb149ec 7 2026-03-20 14:14:25 ~9 min linux/nwaku 📦zip
✔️ bb149ec 7 2026-03-20 14:14:32 ~9 min windows/status-go 📦zip
✔️ bb149ec 7 2026-03-20 14:15:07 ~10 min macos/nwaku 📦zip
✔️ bb149ec 7 2026-03-20 14:21:53 ~16 min tests-rpc 📄log
✔️ bb149ec 7 2026-03-20 14:21:56 ~16 min tests-rpc 📄log
✔️ bb149ec 7 2026-03-20 14:28:29 ~23 min windows/nwaku 📦zip
✔️ bb149ec 7 2026-03-20 14:41:10 ~36 min tests 📄log
✖️ 8aed152 8 2026-03-20 14:11:28 ~3 min tests 📄log
✔️ 8aed152 8 2026-03-20 14:14:08 ~4 min macos/status-go 📦zip
✔️ 8aed152 8 2026-03-20 14:15:06 ~5 min linux/status-go 📦zip
✔️ 8aed152 8 2026-03-20 14:23:39 ~9 min linux/nwaku 📦zip
✔️ 8aed152 8 2026-03-20 14:23:39 ~8 min macos/nwaku 📦zip
✔️ 8aed152 8 2026-03-20 14:26:45 ~11 min windows/status-go 📦zip
✔️ 8aed152 8 2026-03-20 14:47:59 ~18 min windows/nwaku 📦zip
✔️ 8aed152 8 2026-03-20 14:49:24 ~27 min tests-rpc 📄log
✔️ 8aed152 8 2026-03-20 14:50:28 ~28 min tests-rpc 📄log
✔️ 8aed152 8 2026-03-20 15:20:20 ~38 min tests 📄log
✖️ dcc1af8 9 2026-03-20 15:45:57 ~2 min tests 📄log
✔️ dcc1af8 9 2026-03-20 15:46:53 ~4 min linux/status-go 📦zip
✔️ dcc1af8 9 2026-03-20 15:48:27 ~5 min macos/status-go 📦zip
✔️ dcc1af8 9 2026-03-20 15:51:52 ~8 min linux/nwaku 📦zip
✔️ dcc1af8 9 2026-03-20 15:53:07 ~9 min windows/status-go 📦zip
✔️ dcc1af8 9 2026-03-20 15:54:03 ~11 min macos/nwaku 📦zip
✔️ dcc1af8 9 2026-03-20 15:59:25 ~16 min tests-rpc 📄log
✖️ dcc1af8 9 2026-03-20 16:01:51 ~18 min tests-rpc 📄log
✔️ dcc1af8 9 2026-03-20 16:06:31 ~23 min windows/nwaku 📦zip
✖️ dcc1af8 9 2026-03-20 16:19:01 ~35 min tests 📄log
✔️ ded5919 10 2026-03-20 15:55:05 ~6 min macos/status-go 📦zip
✖️ ded5919 10 2026-03-20 15:57:04 ~10 min tests 📄log
✔️ ded5919 10 2026-03-20 15:59:41 ~12 min linux/status-go 📦zip
ded5919 10 2026-03-20 16:02:06 ~8 min windows/status-go 📄log
✔️ ded5919 10 2026-03-20 16:06:30 ~12 min macos/nwaku 📦zip
✔️ ded5919 10 2026-03-20 16:09:35 ~17 min linux/nwaku 📦zip
✔️ ded5919 10 2026-03-20 16:27:29 ~20 min windows/nwaku 📦zip
✖️ ded5919 10 2026-03-20 16:27:32 ~25 min tests-rpc 📄log
✖️ ded5919 10 2026-03-20 16:32:04 ~32 min tests-rpc 📄log
✔️ ded5919 11 2026-03-20 16:54:33 ~11 min windows/status-go 📦zip
✔️ ded5919 10 2026-03-20 16:55:25 ~36 min tests 📄log
✔️ ded5919 11 2026-03-20 16:57:07 ~15 min tests-rpc 📄log
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ 4821ba4 11 2026-03-24 10:56:46 ~4 min linux/status-go 📦zip
✔️ 4821ba4 11 2026-03-24 10:56:56 ~5 min macos/status-go 📦zip
✖️ 4821ba4 11 2026-03-24 11:00:57 ~8 min tests 📄log
✔️ 4821ba4 12 2026-03-24 11:01:02 ~9 min windows/status-go 📦zip
✔️ 4821ba4 12 2026-03-24 11:08:02 ~16 min tests-rpc 📄log
✔️ a1ecaed 12 2026-03-24 13:49:38 ~4 min linux/status-go 📦zip
✖️ a1ecaed 12 2026-03-24 13:49:51 ~4 min tests 📄log
✔️ a1ecaed 12 2026-03-24 13:49:53 ~4 min macos/status-go 📦zip
✔️ a1ecaed 13 2026-03-24 13:57:29 ~11 min windows/status-go 📦zip
✔️ a1ecaed 13 2026-03-24 14:01:33 ~15 min tests-rpc 📄log

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 58.90228% with 307 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.38%. Comparing base (87e5b83) to head (ded5919).

Files with missing lines Patch % Lines
pkg/messaging/waku/gowaku.go 27.94% 48 Missing and 1 partial ⚠️
protocol/communities/manager_archive.go 22.22% 39 Missing and 3 partials ⚠️
services/wallet/service.go 55.55% 29 Missing and 3 partials ⚠️
pkg/backend/pause_play.go 45.28% 27 Missing and 2 partials ⚠️
protocol/messenger.go 23.07% 16 Missing and 4 partials ⚠️
pkg/messaging/waku/api.go 58.33% 13 Missing and 2 partials ⚠️
services/connector/service.go 65.85% 11 Missing and 3 partials ⚠️
pkg/backend/node/get_status_node.go 57.14% 8 Missing and 4 partials ⚠️
internal/timesource/ntp.go 69.69% 7 Missing and 3 partials ⚠️
...kg/messaging/layers/transport/envelopes_monitor.go 44.44% 10 Missing ⚠️
... and 15 more
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #7373      +/-   ##
===========================================
- Coverage    61.46%   61.38%   -0.09%     
===========================================
  Files          824      825       +1     
  Lines       116134   116640     +506     
===========================================
+ Hits         71386    71594     +208     
- Misses       37498    37764     +266     
- Partials      7250     7282      +32     
Flag Coverage Δ
functional 40.23% <27.84%> (-0.14%) ⬇️
unit 55.56% <58.23%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/rpc/client.go 72.45% <100.00%> (+1.38%) ⬆️
...messaging/layers/reliability/datasync/transport.go 92.30% <100.00%> (+3.41%) ⬆️
server/pairing/server.go 63.05% <100.00%> (+0.13%) ⬆️
services/ext/service.go 28.57% <100.00%> (+1.67%) ⬆️
services/newsfeed/service.go 59.89% <100.00%> (+1.47%) ⬆️
pkg/messaging/lifecycle/background_mode.go 96.00% <96.00%> (ø)
protocol/messenger_mailserver.go 42.41% <0.00%> (-0.14%) ⬇️
protocol/messenger_raw_message_resend.go 62.26% <0.00%> (-0.80%) ⬇️
services/wallet/reader.go 71.42% <0.00%> (-0.65%) ⬇️
protocol/local_notifications.go 88.82% <88.00%> (+0.57%) ⬆️
... and 20 more

... and 38 files with indirect coverage changes

@alexjba alexjba force-pushed the feat/reduce-app-work branch 2 times, most recently from 77a9572 to 830ab56 Compare March 16, 2026 14:58
@alexjba alexjba force-pushed the feat/local-notifications branch from 9e77ec1 to 7cc373b Compare March 19, 2026 08:47
@alexjba alexjba force-pushed the feat/reduce-app-work branch from 830ab56 to 2715c40 Compare March 19, 2026 08:49
@alexjba alexjba requested a review from Copilot March 19, 2026 14:31
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a lifecycle-driven pause/resume mechanism across backend, node services, messaging, and network subsystems to reduce non-essential work while the app is backgrounded (battery/resource savings), plus a couple of related operational/UX improvements.

Changes:

  • Introduce pkg/messaging/lifecycle paused-background pub/sub and integrate it across multiple polling loops/tickers (waku, transport, envelopes monitor, backups, NTP, rpc limiter, IPFS, etc.).
  • Add backend/node/service pause/resume hooks (AppStateChange-driven pause/play; StatusNode.PauseBackground/ResumeForeground; service-level implementations/tests).
  • Improve ancillary behavior: richer local notification previews and media server port reuse across foreground/background cycles; RPC client limiter cleanup on stop; simplify mobile AppStateChange API.

Reviewed changes

Copilot reviewed 52 out of 53 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
services/wallet/service_pause_play_test.go Adds wallet service pause/resume noop tests.
services/wallet/service.go Adds wallet pause/resume state + locking and worker start/stop helpers.
services/wallet/reader.go Adds Reader.IsRunning() to support pause/resume decisions.
services/newsfeed/service_test.go Adds newsfeed pause/resume test.
services/newsfeed/service.go Implements pause/resume via Stop/Start + tracks started.
services/ext/service_test.go Tests ext service pause/resume behavior and lifecycle flag updates.
services/ext/service.go Implements pause/resume by toggling messenger paused state.
services/connector/service_test.go Adds connector pause/resume test (includes timing wait).
services/connector/service.go Adds pause/resume via stop/start with mutex + state flags.
services/backup/controller_test.go Adds backup controller lifecycle pause/resume test.
services/backup/controller.go Makes backup ticker respect paused-background lifecycle subscription.
server/server.go Adds lifecycle locking, cached port reuse, and retry logic for foreground start.
server/pairing/server.go Switches pairing BaseServer to embed *server.Server (pointer).
server/pairing/connection_test.go Updates pairing test wiring to match pointer-embedded server.
server/lifecycle_test.go Tests cached-port binding and port reuse across background/foreground.
protocol/messenger_status_updates.go Skips periodic status broadcasts when paused in background.
protocol/messenger_raw_message_resend.go Skips resend loop work while paused.
protocol/messenger_pause_play_test.go Tests messenger paused flag updates lifecycle/global state.
protocol/messenger_mailserver.go Skips missing-message checks while paused.
protocol/messenger_curated_communities.go Skips curated community updates while paused.
protocol/messenger_communities.go Skips multiple community periodic loops while paused.
protocol/messenger.go Adds atomic paused flag + SetPausedBackground, hooks ToForeground/ToBackground, propagates pause to lifecycle/datasync/push client.
protocol/local_notifications_test.go Adds unit tests for message preview extraction and timestamp fallback.
protocol/local_notifications.go Uses safer message preview extraction; avoids nil timestamp panics; improves notification bodies.
protocol/communities/manager_test.go Adds archive interval test for paused lifecycle.
protocol/communities/manager_archive.go Makes archive scheduling/download polling respect paused lifecycle.
pkg/messaging/waku/nwaku.go Pauses connection-change ticker loop via lifecycle subscription.
pkg/messaging/waku/gowaku.go Pauses multiple waku loops; introduces reduced peer exchange behavior while paused.
pkg/messaging/waku/api_test.go Adds tests for shared paused polling loop helper.
pkg/messaging/waku/api.go Adds lifecycle-aware polling helper for RPC message subscription polling.
pkg/messaging/lifecycle/background_mode_test.go Adds tests for paused-background pub/sub semantics and concurrency.
pkg/messaging/lifecycle/background_mode.go Implements global paused flag + subscriber management with coalescing.
pkg/messaging/layers/transport/transport_test.go Tests transport filter cleanup loop pause/resume behavior.
pkg/messaging/layers/transport/transport.go Adds lifecycle-aware filter cleanup loop + test hook interval/fn.
pkg/messaging/layers/transport/envelopes_monitor_test.go Tests envelopes retry loop skipping when paused.
pkg/messaging/layers/transport/envelopes_monitor.go Makes envelopes retry loop lifecycle-aware.
pkg/messaging/layers/reliability/datasync/transport.go Adds paused-background control over datasync tick rate + uses dynamic offset in backoff calculation.
pkg/backend/pre_login.log Adds a pre-login log artifact file.
pkg/backend/pause_play_test.go Adds backend lifecycle/AppStateChange tests for paused/stopped transitions.
pkg/backend/pause_play.go Introduces backend lifecycle state machine + pause/resume orchestration.
pkg/backend/node/pause_play_test.go Tests StatusNode pause/resume calls services implementing lifecycle hooks.
pkg/backend/node/get_status_node.go Adds background lifecycle interface + PauseBackground/ResumeForeground on StatusNode.
pkg/backend/geth_backend.go Integrates pause/resume into AppStateChange; adds lifecycle locking and state transitions.
mobile/status.go Simplifies mobile AppStateChange API to single call returning JSON response; removes V2.
mobile/requests/app_state_change_test.go Removes request validation tests (AppStateChangeV2 removal).
mobile/requests/app_state_change.go Removes request schema/validator for AppStateChangeV2.
internal/timesource/ntp_test.go Tests NTP periodic runner pausing/resuming via lifecycle.
internal/timesource/ntp.go Makes NTP periodic runner lifecycle-aware (timer-based).
internal/rpc/client.go Stops and clears per-provider limiters during client Stop.
internal/rpc/chain/rpclimiter/rpc_limiter_test.go Adds lifecycle pause/resume test for RPC limiter release loop.
internal/rpc/chain/rpclimiter/rpc_limiter.go Pauses limiter ticker loop via lifecycle subscription.
internal/ipfs/ipfs_test.go Tests IPFS downloader dispatcher pausing/resuming via lifecycle.
internal/ipfs/ipfs.go Makes IPFS task dispatcher lifecycle-aware.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@alexjba alexjba force-pushed the feat/local-notifications branch 2 times, most recently from 9bfce9b to 471c25e Compare March 20, 2026 10:39
Base automatically changed from feat/local-notifications to develop March 20, 2026 12:42
@alexjba alexjba force-pushed the feat/reduce-app-work branch 4 times, most recently from dcc1af8 to ded5919 Compare March 20, 2026 15:43
@alexjba alexjba marked this pull request as ready for review March 20, 2026 16:45
Copy link
Member

@jrainville jrainville left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huge work. Very nice!

My only concern is with the performance of Desktop apps. Most Desktop apps don't care that much about being in the background or foreground.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The archive cannot work on mobile, so I'm not sure if those changes are needed. I know we already have some code where we speed up the archive in the background since the app is not in use. Maybe this code comes in conflict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's true. It's not needed for mobile builds since we're using disable_torrent.
But I'd keep it for completeness. Otherwise we'll need some explicit rules for what's allowed to run in "background" mode.
Could be an overkill at this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this code comes in conflict?

I don't see any conflict with other mechanisms.

datasync.SetPausedBackground(paused)
if m.pushNotificationClient != nil {
if paused {
m.pushNotificationClient.Offline()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm misunderstanding, but shouldn't we want the push notifications to keep working as usual?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is communicating with the push notifications server to send push notifications to other devices or to update config options. We shouldn't need it while the app is in the background.

It's not needed at all to receive push notifications

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of those are only run on Desktop. I think on Desktop, it's fine to do most of these operations in the background.

I wonder if we should make toBackground smarter, where it check whether we are charging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should make toBackground smarter, where it check whether we are charging?

We can do that, yes! Although not sure what would be the benefit.

server/server.go Outdated
// Retry a few times to preserve stable URL reuse semantics.
if s.config != nil && s.config.AddrPort.Port() == 0 && s.cachedPort != 0 && errors.Is(err, syscall.EADDRINUSE) {
for i := 0; i < 10; i++ {
time.Sleep(20 * time.Millisecond)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function called syncly? If so, we shouldn't use sleep or we should put it in a routine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I have a better way of dealing with this without "retry until it works".

We can use SO_REUSEADDR to bind immediately https://man7.org/linux/man-pages/man7/socket.7.html.

Will update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. I'll need someone to test windows though. It seems the API is a little bit different there.

@alexjba
Copy link
Contributor Author

alexjba commented Mar 24, 2026

My only concern is with the performance of Desktop apps. Most Desktop apps don't care that much about being in the background or foreground.

That's true. And desktop apps don't need to use the AppStateChange API.
In the first phase it will only be used on android.

alexjba added 5 commits March 24, 2026 15:45
Introduce a lifecycle-based pause/resume mechanism to reduce non-essential
work when the app goes to background, improving battery and resource usage.

- Add pkg/messaging/lifecycle for SubscribePausedBackground/SetPausedBackground
- Add backend pause/play (pauseLocked/resumeLocked) driven by AppStateChange
- StatusNode.PauseBackground/ResumeForeground coordinate services via
  backgroundLifecycle interface
- Messenger.ToForeground/ToBackground broadcast to lifecycle, datasync,
  and push notification client
- Pause tickers/loops in: centralizedmetrics, ipfs, rpc limiter, ntp,
  backup, transport, envelopes monitor, connector, wallet, newsfeed, ext
- Simplify mobile AppStateChange (remove AppStateChangeV2, single API)
- RPC client: clean up limiters on Stop
BaseServer now receives a pointer since it owns a mutex.

+ adding more tests for the lifecycle flows
- remove log file
- Use `syscall.SO_REUSEADDR` for media server to allow quick rebind on the same port
@alexjba alexjba force-pushed the feat/reduce-app-work branch from 4821ba4 to a1ecaed Compare March 24, 2026 13:45
defer common.LogOnPanic()
ticker := time.NewTicker(time.Second / maxRequestsPerSecond)
defer ticker.Stop()
sub := messaginglifecycle.SubscribePausedBackground()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These blocks looks identical. Maybe extract it to an utility function ?

func RunPausableTicker(ticker *time.Ticker, quit <-chan struct{}, action func()) {
    sub := SubscribePausedBackground()
    defer sub.Unsubscribe()
    paused := <-sub.C()
    var tickerC <-chan time.Time
    if !paused {
        tickerC = ticker.C
    }
    for {
        select {
        case pausedState, ok := <-sub.C():
            if !ok { return }
            paused = pausedState
            if paused { tickerC = nil } else { tickerC = ticker.C }
        case <-tickerC:
            action()
        case <-quit:
            return
        }
    }
}

return nil
}

func (s *Service) PauseBackground() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe extract common lifecycle flow to a mixin?


package common

import "sync"

type LifecycleMixin struct {
    mu     sync.Mutex
    started bool
    paused  bool
}

func (m *LifecycleMixin) PauseBackground(onPause func() error) error {
    m.mu.Lock()
    defer m.mu.Unlock()
    if !m.started || m.paused {
        return nil
    }
    if err := onPause(); err != nil {
        return err
    }
    m.paused = true
    return nil
}

func (m *LifecycleMixin) ResumeForeground(onResume func() error) error {
    m.mu.Lock()
    defer m.mu.Unlock()
    if !m.started || !m.paused {
        return nil
    }
    if err := onResume(); err != nil {
        return err
    }
    m.paused = false
    return nil
}

func (m *LifecycleMixin) MarkStarted() { m.mu.Lock(); m.started = true; m.paused = false; m.mu.Unlock() }
func (m *LifecycleMixin) MarkStopped() { m.mu.Lock(); m.started = false; m.paused = false; m.mu.Unlock() }
func (m *LifecycleMixin) IsPaused() bool { m.mu.Lock(); defer m.mu.Unlock(); return m.paused }

@friofry friofry requested a review from igor-sirotin March 25, 2026 12:32
@friofry
Copy link
Contributor

friofry commented Mar 25, 2026

It would be great to hear what @igor-sirotin thinks. Just to confirm the order of starting and stopping services

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a pkg/pubsub that I think can be used for this

Comment on lines +13 to +18
const (
AppLifecycleStopped AppLifecycleState = "stopped"
AppLifecycleRunning AppLifecycleState = "running"
AppLifecyclePausedBackground AppLifecycleState = "paused_background"
AppLifecycleResumingForeground AppLifecycleState = "resuming_foreground"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last time we were looking at status-go API, we were planning to remove any "app state" notion from the backend — endpoints like AppStateChange and ConnectionChange.

IMO, backend should be agnostic of app's state. Instead, it can have specific behaviour-changing actions, e.g. pauseService(serviceName) or resetConnection. It is application-level details that there is some "background mode", "battery-saving mode" or "expensive connection".

Having such stuff in backend is quite attractive, but it messes the frontend-backend split.

This was created during the "let's implement as much as we can in backend" paradigm, when we had 2 clients. But there should be a concrete border-line of what goes where. E.g. for bots a "background mode" makes no sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. for bots a "background mode" makes no sense.

Unless it runs on your phone 😄

I see the point! For a similar reason I've found it difficult to exclude some things that are not necessary for mobile chat - like archive manager. Because we're missing a proper inventory and it's easier to track active services and loops if it's all or nothing.

Ok, so in conclusion the best way forward would be (please correct me if I got it wrong):

  • create some sort of inventory for services and capabilities for each service. Basically status-go would expose a list of capabilities and the client would then be able to do a granular control.

  • add APIs to pause (or stop) services

  • add APIs to control some service capabilities.

  • configure the client to do a granular pause while in the background.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I think this would be better 👍

return nil
}

func (s *Service) PauseBackground() error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just Pause and Resume?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants