fix(02-11): gate three leaked background pollers to activate/deactivate
Demo images / Build & push demo images (push) Has been cancelled
Demo images / Build & push demo images (push) Has been cancelled
Fleet.vue's useFleetData() (60s telemetry.fleet-status/-alerts poll), Server.vue's FipsNetworkCard.vue (15s fips.status poll), and Web5.vue's Web5Monitoring.vue (30s system.stats poll — redundant with Home.vue's own correctly-gated 10s poll of the same store) all armed their setInterval in onMounted and only disarmed it in onUnmounted/ onBeforeUnmount. That was harmless before 02-04 registered their owning views in KEEP_ALIVE_PATHS (the view was destroyed on every tab-away, so the teardown hook fired every time); once KeepAlive keeps the instance alive, the teardown hook never fires again and the poll ran forever in the background regardless of which dashboard tab was showing. Gated arm/disarm to onActivated/onDeactivated, mirroring Server.vue's own vpnPollInterval fix from 02-04 exactly. Added regression tests to keepAliveLifecycle.test.ts mounting each real component under a synthetic KeepAlive with fake timers; confirmed RED against the pre-fix code (git stash) before confirming GREEN with the fix restored. Full suite (95 files/788 tests), type-check and build all green. keepAliveTabs.test.ts is byte-for-byte unmodified. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e5c38866ca
commit
2c25e512a7
@@ -1,6 +1,6 @@
|
||||
/** Composable encapsulating fleet telemetry data fetching, types, and utilities */
|
||||
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, onActivated, onDeactivated, watch } from 'vue'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import type { ChartDataset } from '@/components/LineChart.vue'
|
||||
|
||||
@@ -480,17 +480,44 @@ export function useFleetData() {
|
||||
|
||||
// --- Lifecycle ---
|
||||
|
||||
// 02-11 gap closure: `startAutoRefresh()`'s 60s poll used to be armed only
|
||||
// in onMounted and disarmed only in onUnmounted — harmless before Fleet
|
||||
// joined KEEP_ALIVE_PATHS (02-04), since leaving the tab fully unmounted
|
||||
// this composable's owning component and onUnmounted fired every time.
|
||||
// Once Fleet is KeepAlive'd, onUnmounted never fires again after the first
|
||||
// visit, so the poll ran forever in the background regardless of tab
|
||||
// visibility — the exact off-screen-drain class 02-04's own audit
|
||||
// convention exists to prevent, missed here because that audit grepped
|
||||
// Fleet.vue itself (which has no lifecycle side effects of its own) and
|
||||
// never extended to this composable it delegates to. Arm/disarm now
|
||||
// follows activate/deactivate, mirroring Server.vue's vpnPollInterval
|
||||
// fix from 02-04; `startAutoRefresh()` already clears any existing timer
|
||||
// first, so calling it from both onMounted and onActivated on a fresh
|
||||
// KeepAlive-wrapped mount is safe and idempotent.
|
||||
function armFleetPoll() {
|
||||
if (autoRefresh.value) startAutoRefresh()
|
||||
}
|
||||
function disarmFleetPoll() {
|
||||
stopAutoRefresh()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
updateChartWidth()
|
||||
window.addEventListener('resize', updateChartWidth)
|
||||
await refreshAll()
|
||||
if (autoRefresh.value) {
|
||||
startAutoRefresh()
|
||||
}
|
||||
armFleetPoll()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
armFleetPoll()
|
||||
})
|
||||
|
||||
onDeactivated(() => {
|
||||
disarmFleetPoll()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopAutoRefresh()
|
||||
disarmFleetPoll()
|
||||
window.removeEventListener('resize', updateChartWidth)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user