fix(host-service): recover v2 ports promptly after host-service restart

Merged
KI
4mo ago
+98-13

Problem

After a host-service restart (installed app, v1.12.6+), v2 dev-server ports don't show in the sidebar until you select the workspace. In production the detached pty-daemon keeps dev servers alive across the restart, so the ports genuinely exist — they're just not scanned.

Root cause

#5325 added a reaper-driven port-scan reconcile to register daemon-adopted sessions without a renderer attach. But that reconcile runs only once at startup and then every 5 minutes (it's embedded in the orphan reaper's REAP_INTERVAL_MS loop). Right after a restart the just-adopted daemon may not yet list its sessions the instant that single startup pass runs — so nothing gets registered, and the next attempt is 5 minutes away. Selecting the workspace masks it by attaching a renderer, which registers the terminal directly.

Ruled out: the client visibility gate (all its collections are persisted and populate on cold start) and killStaleDaemonForDev (dev-only; production adopts the daemon).

Fix

Decouple the port-scan sync from the 5-minute orphan-reap cadence and run it a few times over the first ~90s after startup (2/5/10/20/45/90s), so restored ports register promptly without waiting for the reap tick or an attach.

  • Extract syncPortScans() (daemon.list + row join + applyPortScanSync); the reap pass reuses its result, so no extra daemon.list() there.
  • Warm-up runs only the port-scan sync — never the orphan reap — so it doesn't disturb the reaper's two-pass rowless-orphan clock.
  • Log registered N unattached daemon session(s) so post-restart recovery is observable in the field.

Validation

  • planPortScanSync logic unchanged (existing tests pass); added a schedule test asserting the warm-up is multi-shot, strictly increasing, and fully precedes the first reap tick.
  • bun run lint clean, @superset/host-service typecheck passes.

Caveat: I could not end-to-end reproduce the production timing locally (dev's killStaleDaemonForDev destroys the daemon on restart, and an adoption-mode repro was too flaky to finish). This fix targets the most likely cause — the reconcile cadence — and the new log lets us confirm in a real build: if it logs registrations after restart and ports appear, done; if it logs nothing, the daemon isn't listing adopted sessions and we dig into adoption instead.


Open in Stage

Summary by cubic

Recover v2 dev-server ports promptly after a @superset/host-service restart. Ports now appear within ~90s without opening the workspace.

  • Bug Fixes

    • Decoupled port-scan sync from the 5-minute orphan reaper; run warm-up syncs at 2/5/10/20/45/90s after startup without running the orphan reap.
    • Extracted syncPortScans(); the reaper reuses its result to avoid an extra daemon.list(); logs registered unattached sessions and warns on warm-up sync failures.
    • Coalesced concurrent port-scan syncs so warm-up timers and the reap pass share one in-flight run, preventing transient empty lists from unregistering sessions.
    • On shutdown, clear the warm-up timers and interval.
  • Refactors

    • Updated stale inline comment around port-scan sync after the warm-up refactor.

Summary by CodeRabbit

  • New Features
    • Added a port-scan warm-up phase shortly after host-service restart to retry syncing open ports before the main reap cycle begins.
  • Bug Fixes
    • Improved reliability by consolidating concurrent port-scan sync runs and ensuring all scheduled warm-up timers are cleared during shutdown.
  • Tests
    • Added coverage validating the warm-up schedule has multiple strictly increasing offsets and that every warm-up delay occurs before the first reap interval.