Skip to content

luci-app-pbr-1.2.3: refresh stale service status after Save & Apply - #33

Merged
egc112 merged 1 commit into
1.2.3from
egc-luci-pbr
Aug 8, 2026
Merged

luci-app-pbr-1.2.3: refresh stale service status after Save & Apply#33
egc112 merged 1 commit into
1.2.3from
egc-luci-pbr

Conversation

@egc112

@egc112 egc112 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Saving settings fires pbr's procd config.change trigger, which reloads the service asynchronously. LuCI reloads the page as soon as the apply completes, so getInitStatus() lands mid-reload and reports the service as stopped -- and nothing ever re-checked it, leaving a stale "Stopped" in the status box until the user refreshed the page by hand. The service control buttons already handled this via pollServiceStatus() in pbr/status.js; the plain settings-save path went through LuCI's generic form flow and had no equivalent.

Re-check the status from render(), but only while it looks like that transient state (enabled but not running), stopping as soon as it settles and giving up after ~90s. Intervals ramp 1.5s -> 3s -> 5s, so the common case flips within a couple of seconds without competing for CPU with the reload being waited on.

Gating on enabled && !running matters for cost: getInitStatus() is expensive on the router, since the rpcd handler builds a fresh pbr instance per request and so re-runs full platform detection plus an nft list table dump on every call. A normally-running service therefore costs no extra RPC calls at all.

Two deliberate choices worth recording:

  • setTimeout rather than LuCI's poll module, matching pollServiceStatus() in pbr/status.js. A registered poll drives LuCI's global auto-refresh indicator, which then sits at "Paused" once unregistered, reading as though the page had stalled.
  • The status box is re-rendered only once the state settles, not on every tick, so the service control buttons inside it aren't torn out from under the user while they are looking at a stopped service.

Known limitation: this covers stale "Stopped" only. If the page loads while the service is running and it later fails to come back, the box stays stale until a manual refresh -- the trade for zero polling in the healthy case. A service the user stopped deliberately also re-checks for ~90s before giving up, which is bounded and self-terminating.

Saving settings fires pbr's procd config.change trigger, which reloads
the service asynchronously. LuCI reloads the page as soon as the apply
completes, so getInitStatus() lands mid-reload and reports the service
as stopped -- and nothing ever re-checked it, leaving a stale "Stopped"
in the status box until the user refreshed the page by hand. The
service control buttons already handled this via pollServiceStatus() in
pbr/status.js; the plain settings-save path went through LuCI's generic
form flow and had no equivalent.

Re-check the status from render(), but only while it looks like that
transient state (enabled but not running), stopping as soon as it
settles and giving up after ~90s. Intervals ramp 1.5s -> 3s -> 5s, so
the common case flips within a couple of seconds without competing for
CPU with the reload being waited on.

Gating on enabled && !running matters for cost: getInitStatus() is
expensive on the router, since the rpcd handler builds a fresh pbr
instance per request and so re-runs full platform detection plus an
`nft list table` dump on every call. A normally-running service
therefore costs no extra RPC calls at all.

Two deliberate choices worth recording:

 - setTimeout rather than LuCI's poll module, matching
   pollServiceStatus() in pbr/status.js. A registered poll drives
   LuCI's global auto-refresh indicator, which then sits at "Paused"
   once unregistered, reading as though the page had stalled.
 - The status box is re-rendered only once the state settles, not on
   every tick, so the service control buttons inside it aren't torn out
   from under the user while they are looking at a stopped service.

Known limitation: this covers stale "Stopped" only. If the page loads
while the service is running and it later fails to come back, the box
stays stale until a manual refresh -- the trade for zero polling in the
healthy case. A service the user stopped deliberately also re-checks
for ~90s before giving up, which is bounded and self-terminating.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: Erik Conijn <egc112@msn.com>
@egc112

egc112 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

Co-Pilot comment:

LGTM.

This reliably fixes the stale "Stopped" status after Save & Apply by re-checking status only when enabled && !running, with a bounded backoff and sensible ramping. Gating the checks to avoid extra RPCs and using setTimeout to avoid poll UI churn are both reasonable choices. No blockers from me.

@egc112
egc112 merged commit 43158bb into 1.2.3 Aug 8, 2026
3 checks passed
@egc112
egc112 deleted the egc-luci-pbr branch August 14, 2026 06:59
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.

1 participant