Skip to content

fix(docker): keep "Check for updates" active for the full walk (v0.8.3) - #14

Merged
Inch-high merged 2 commits into
mainfrom
fix/docker-check-updates-poll-race
Jun 1, 2026
Merged

Inch-high merged 2 commits into
mainfrom
fix/docker-check-updates-poll-race

Conversation

@Inch-high

Copy link
Copy Markdown
Owner

Patch v0.8.3 — fix the /Docker "Check for updates" button finishing too quickly

Symptom

On a 40+ container host, "Check for updates" looked like it finished in ~2 seconds — out of sync with the real per-image registry walk, which takes much longer.

Root cause (front-end, deterministic)

The backend is correct: stock DockerTemplates::getAllInfo($reload=true) calls reloadUpdateStatus($image) per container (the real HTTPS digest fetch), confirmed against the live host. The bug was in the status-poll loop in md-docker-page.ts:

if (status.running) {
  this._checkPollHandle = window.setTimeout(tick, ...);
  return;                       // returns THROUGH the finally...
}
...
} finally {
  this._checkingUpdates = false;   // ...which fired on the FIRST poll, even while running
}

A return inside try still runs finally, so _checkingUpdates (a Lit @state()) was reset after the first 2s tick. The button reverted to idle while the worker kept walking in the background. A secondary issue: POLL_MAX_MS was a hard 60s total cap, shorter than a real 40+ container walk, so it could also cut the post-check snapshot refresh off mid-walk and read stale updated flags.

Fix

  • Remove the misplaced finally; clear _checkingUpdates only on genuine terminal paths (completed / poll error / watchdog).
  • Conclude only once the run actually finished — observed running:true, or a finishedAt newer than a pre-launch baseline (keeps small/fast hosts snappy). Extracted as a pure checkUpdatesCompleted() helper.
  • Turn POLL_MAX_MS into an inactivity watchdog, reset on each running:true, so a long-but-alive walk isn't cut off; a worker that never establishes (or wedges) its lock still bails.

Backend left untouched.

Tests

  • New checkUpdatesCompleted() + 5 unit cases in check-updates.test.ts.
  • Full TS suite green (456 passed); Biome clean.

Verified live

Deployed to a 43-container Unraid host. Triggered "Update all" on the 2 flagged containers: cloudflareddns (running) updated + restarted; Nginx-Proxy-Manager (stopped, autostart off) updated + correctly left stopped. unraid-update-status.json flipped both false→true and webui-info was rewritten — badges clear as expected.

Release

Bumped package.json, package-lock.json, and the .plg <!ENTITY version> to 0.8.3, with a matching ###v0.8.3 changelog entry in the .plg <CHANGES> block. Tag v0.8.3 to publish after merge.

🤖 Generated with Claude Code

Inch-high and others added 2 commits June 1, 2026 19:11
The check-updates status poll cleared the "Checking…" state on its first
2s tick: a `return` inside the try fell through to a `finally` that reset
_checkingUpdates. On a 40+ container host the button flipped back to idle
almost immediately while the per-image registry walk was still running —
the "check runs too quickly / not in sync with N containers" symptom.

- Remove the misplaced finally; clear _checkingUpdates only on genuine
  terminal paths (completed, poll error, watchdog).
- Conclude only once the run has actually finished: observed running:true,
  or a finishedAt newer than a pre-launch baseline (keeps fast hosts snappy).
- Turn the 60s hard cap into an inactivity watchdog reset on each
  running:true, so a long but live walk isn't cut off mid-flight (which
  also avoided reading stale `updated` flags from a half-written cache).
- Extract checkUpdatesCompleted() pure helper + 5 unit cases.

Backend left untouched — verified correct against stock getAllInfo /
reloadUpdateStatus on a live host. Bump to v0.8.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hdog

The v0.8.3 fix extracted checkUpdatesCompleted() and unit-tested the
decision, but the regression itself lived in the poll *loop* wiring
(_pollCheckUpdates) — which had no test. Add an integration test that
drives the real loop with fake timers + a mocked actions module
(keeping the real checkUpdatesCompleted), pinning the behaviour the pure
helper can't catch:
- button stays "Checking…" across a premature running:false → running:true
  → done (the exact first-tick moment the old `finally` reset it),
- fast-host conclude on a fresh finishedAt,
- poll error releases the button,
- inactivity watchdog bails only after POLL_MAX_MS of no sign of life,
- a live worker runs well past the old 60s hard cap,
- a hidden tab pauses without tripping the watchdog.

Also fix a hidden-tab edge in _pollCheckUpdates: the watchdog check ran
before the document.hidden check, so a backgrounded tab during a long
walk counted as "no sign of life" and bailed mid-flight (refreshing a
half-written cache — the stale-flag symptom this loop exists to avoid).
Move the hidden check first and grant a fresh watchdog window on return
to visibility via a wasHidden flag.

Full TS suite green (462 passed); Biome clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Inch-high
Inch-high merged commit 6b17a99 into main Jun 1, 2026
2 checks passed
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