Skip to content

fix(node): suppress OTA activity while the node is offline - #4168

Merged
Apollon77 merged 3 commits into
mainfrom
fix/software-update-suppress-while-offline
Jul 31, 2026
Merged

fix(node): suppress OTA activity while the node is offline#4168
Apollon77 merged 3 commits into
mainfrom
fix/software-update-suppress-while-offline

Conversation

@Apollon77

@Apollon77 Apollon77 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Minor hardening in SoftwareUpdateManager.

The update paths guarded only against disposal (internal.closed), so a node that had gone offline but was not yet disposed kept doing update work: collecting per-peer update info, downloading images, walking the consent list, firing queued updates, and reacting to OTA status changes.

The guard now covers both states — raised on lifecycle.goingOffline, lowered again on lifecycle.online so a node that comes back resumes normally. internal.closed is renamed to internal.suppressUpdates since it no longer means "disposed"; [Symbol.asyncDispose] still raises it, so the previous behavior is a subset.

Two gaps found in review and fixed on top:

  • The flag started out lowered, so update work could still run between initialize() and the first online transition — the very window it exists to cover. It now starts raised.
  • Going offline left the OtaAnnouncements instance alive. It runs its own timers and writes to peers, so it kept announcing while offline regardless of the flag. It is now closed on goingOffline along with the update-check timer; #nodeOnline already installs a fresh instance.

The queue timer is deliberately left running: #triggerQueuedUpdate returns immediately while suppressed and re-arms itself only when there is queue work, so stopping it would gain nothing and could delay a resumed queue.

Also tightens one == undefined to === undefined.

Checklist

  • npm test passes (full suite, all packages)
  • npm run format-verify and npm run lint pass
  • npm run build -- --clean passes
  • Tests added or updated — none; no new externally observable behavior beyond the lifecycle guard, and SoftwareUpdateManager has no existing unit-test harness for lifecycle transitions
  • CHANGELOG updated — not applicable, internal hardening with no API change

No log file attached: this is not an issue-driven fix but a proactive hardening of the offline path.

🤖 Generated with Claude Code

The update paths guarded only against disposal, so a node that went offline
kept collecting update info, downloading images and reacting to OTA status
changes until it was disposed. The guard now covers both states: it is raised on
goingOffline and lowered again on online, and internal.closed becomes
internal.suppressUpdates to say what it actually gates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 19:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the node-side OTA provider logic by suppressing SoftwareUpdateManager activity while a ServerNode is offline, resuming when it comes back online, and by clarifying intent via a rename from internal.closed to internal.suppressUpdates.

Changes:

  • Add lifecycle handling for goingOffline/online to toggle OTA suppression.
  • Replace internal.closed guards with internal.suppressUpdates in update paths and status handling.
  • Tighten one undefined check from == undefined to === undefined.

Comment thread packages/node/src/behavior/system/software-update/SoftwareUpdateManager.ts Outdated
Apollon77 and others added 2 commits July 30, 2026 21:28
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
suppressUpdates started out lowered, so update work could run between
initialize() and the first online transition — the very window the flag exists
to cover. It now starts raised and #nodeOnline lowers it.

Going offline also left the announcement instance alive. OtaAnnouncements runs
its own timers and writes to peers, so it kept announcing while offline
regardless of the flag. It is now closed on goingOffline, alongside the update
check timer; #nodeOnline already installs a fresh instance.

The queue timer is left running: #triggerQueuedUpdate returns immediately while
suppressed, and it re-arms itself only when there is queue work.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

packages/node/src/behavior/system/software-update/SoftwareUpdateManager.ts:216

  • #nodeGoingOffline is a reactor on NodeLifecycle.goingOffline (an AsyncObservable awaited by Node.stop()), but it awaits announcements.close(). OtaAnnouncements.close() awaits any in-flight announcement promise, which can block the node’s offline transition until network timeouts complete. Since close() already stops timers before its internal await, you can fire-and-forget the close here (with a .catch) to stop future announcements without delaying the lifecycle transition.
        // Announcements run on their own timers and write to peers, so the suppression flag alone does not stop them.
        // #nodeOnline installs a fresh instance.
        await this.internal.announcements?.close();
        this.internal.announcements = undefined;

packages/node/src/behavior/system/software-update/SoftwareUpdateManager.ts:212

  • suppressUpdates is raised on goingOffline, and queryUpdates() now returns early when suppressed, but checkForUpdates() still unconditionally runs #cleanupObsoleteUpdates() afterward (see checkForUpdates around line ~401). This means storage scans/deletions can still run while the node is offline/suppressed (e.g. if checkForUpdates() is invoked directly, or if an in-flight timer callback continues after checkForUpdateTimer.stop()). Consider adding an early if (this.internal.suppressUpdates) return; guard in checkForUpdates() (and/or at the top of #cleanupObsoleteUpdates()).
    async #nodeGoingOffline() {
        this.internal.suppressUpdates = true;
        this.internal.checkForUpdateTimer?.stop();

@mergify

mergify Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@Apollon77
Apollon77 merged commit bb1c3bd into main Jul 31, 2026
39 checks passed
@Apollon77
Apollon77 deleted the fix/software-update-suppress-while-offline branch July 31, 2026 19:37
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.

2 participants