Skip to content

Commit e316f12

Browse files
fix(live): bind "X / Y nodes tested" denominator to run total, not per-batch snapshot
The "X / Y nodes tested" headline (and the lsTested / Network Nodes tiles) sourced their denominator from _cb.snapshotSize, the PER-BATCH count. On a resumed batch the server emits batch:start with snapshotSize = remaining- untested nodes only (continuous.js), while the cumulative tested count (X = deduped resultsArr.length, including pre-resume rows replayed on init) needs the FULL run total. Result after stop->resume: Y showed the previous run's total and only caught up once the next full batch:start landed. Root cause: the denominator was bound to the wrong scope. The authoritative full-sweep total is state.totalNodes (maintained across resume by pipeline.js and broadcast immediately via the state event). Three callers (cbRender, renderLiveStats, applyHeaderStatsFromState) computed the denominator independently with divergent priority. Fix: - live.html: add runTotalNodes() — single source of truth preferring _liveState.totalNodes, then _cb.snapshotSize, then resultsArr.length. Route all three callers through it. - pipeline.js: zero state.totalNodes in the fresh-run (non-resume) reset so the pre-scan state broadcasts no longer carry the prior run's total (which the new denominator priority would otherwise flash as a wrong "Y" until the scan completes). Falls back to "—" until the sweep size is known. - live.html: defense-in-depth — resetLiveForNewRun() clears _liveState.totalNodes so the run-number-change path cannot carry the old total before mergeLiveState. Resume path unchanged. Tests 188/31/45/35 pass; inline scripts parse clean.
1 parent 6935557 commit e316f12

2 files changed

Lines changed: 44 additions & 13 deletions

File tree

audit/pipeline.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,14 @@ export async function runAudit(resume, state, broadcast, preloadedNodes = null,
632632
state.passedBaseline = 0;
633633
state.nodeSpeedHistory = [];
634634
state.baselineHistory = [];
635+
// A fresh run's sweep total isn't known until after the online scan
636+
// (set at line ~760). Until then totalNodes must read 0, NOT the prior
637+
// run's value — several broadcast('state') calls fire before the scan
638+
// (balance, baseline, node-fetch), each carrying the new activeRunNumber.
639+
// /live now prefers state.totalNodes as its "X / Y nodes tested"
640+
// denominator, so a lingering prior-run total would flash a wrong "Y"
641+
// (e.g. "0 / 1040") until the scan completes. Zero falls back to "—".
642+
state.totalNodes = 0;
635643
saveResults(state);
636644
}
637645

live.html

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,8 +1606,30 @@ <h1 class="live-page-title">
16061606
}, 1000);
16071607
}
16081608

1609+
// Single source of truth for the run-wide sweep denominator (the "Y" in
1610+
// "X / Y nodes tested" and "X / Y Nodes"). Prefer state.totalNodes — the
1611+
// full per-run sweep the pipeline maintains across stop→resume and
1612+
// broadcasts via the `state` event — over _cb.snapshotSize. The latter is
1613+
// the *per-batch* count, and on a RESUMED batch (continuous.js emits
1614+
// `snapshotSize: viableNodes.length`) it is only the remaining-untested
1615+
// nodes. The tested count (X = deduped resultsArr.length) is cumulative
1616+
// across the whole run, including pre-resume rows replayed on `init`, so
1617+
// pairing it with a remainder-only denominator made "X / Y" lag (showing
1618+
// the previous run's total) until the next full batch:start landed. Three
1619+
// callers (cbRender, renderLiveStats, applyHeaderStatsFromState) computed
1620+
// this independently with divergent priority; routing them through one
1621+
// helper keeps them from drifting again. Falls back to the per-batch
1622+
// snapshot, then the row count, when the run total isn't known yet.
1623+
function runTotalNodes() {
1624+
const total = _liveState && Number(_liveState.totalNodes);
1625+
if (total > 0) return total;
1626+
if (_cb.snapshotSize > 0) return _cb.snapshotSize;
1627+
const arr = Array.isArray(resultsArr) ? resultsArr : [];
1628+
return arr.length || 0;
1629+
}
1630+
16091631
function cbRender() {
1610-
const snap = _cb.snapshotSize;
1632+
const snap = runTotalNodes();
16111633
// Single source of truth: the deduped resultsArr (rows upserted by
16121634
// address) — same approach admin.html uses for its Tested tile. Server
16131635
// `state.testedNodes` can double-count retries / counter-increment
@@ -1685,12 +1707,11 @@ <h1 class="live-page-title">
16851707
const set = (id, v) => { const el = document.getElementById(id); if (el) el.textContent = v; };
16861708

16871709
const arr = Array.isArray(resultsArr) ? resultsArr : [];
1688-
// Snapshot total for THIS run. Prefer the current-batch snapshot from
1689-
// `batch:start` and `state.totalNodes` (admin's per-run snapshot). Both
1690-
// reflect the size of the active sweep, not the chain-wide node count.
1691-
const snap = _cb.snapshotSize ||
1692-
(_liveState && Number(_liveState.totalNodes)) ||
1693-
arr.length || 0;
1710+
// Run-wide sweep total for THIS run (the "Y" denominator). See
1711+
// runTotalNodes() — prefers state.totalNodes so a resumed batch's
1712+
// remainder-only snapshotSize can't shrink the denominator below the
1713+
// cumulative tested count.
1714+
const snap = runTotalNodes();
16941715

16951716
// "Connected" = node returned mbps>0 (handshake + speed both succeeded).
16961717
const connected = arr.filter(r => r.actualMbps != null && r.actualMbps > 0).length;
@@ -1939,12 +1960,9 @@ <h1 class="live-page-title">
19391960
} else {
19401961
setHeaderBaseline(null);
19411962
}
1942-
// Network Nodes — total snapshot size for current batch (state.totalNodes
1943-
// tracks scanned/online; fall back to _cb.snapshotSize, then resultsArr.)
1944-
const totalNodes =
1945-
(_liveState && _liveState.totalNodes) ||
1946-
_cb.snapshotSize ||
1947-
resultsArr.length;
1963+
// Network Nodes — run-wide sweep total. Shared denominator (prefers
1964+
// state.totalNodes, falls back to _cb.snapshotSize, then resultsArr).
1965+
const totalNodes = runTotalNodes();
19481966
// Only overwrite if we actually have a live value; otherwise leave
19491967
// whatever loadHeaderStats() painted from /api/public/stats.
19501968
if (totalNodes) set('hdrNetworkNodes', Number(totalNodes).toLocaleString());
@@ -2071,6 +2089,11 @@ <h1 class="live-page-title">
20712089
_cb.passed = 0;
20722090
_cb.failed = 0;
20732091
_cb.snapshotSize = 0;
2092+
// Drop the prior run's sweep total too: runTotalNodes() now prefers
2093+
// _liveState.totalNodes, so leaving it set would carry the old "Y" into
2094+
// the new run until the next state event overwrites it. The immediately
2095+
// following mergeLiveState() repopulates it from the fresh run's state.
2096+
if (_liveState) _liveState.totalNodes = 0;
20742097
cbRender();
20752098
try { localStorage.removeItem(LIVE_CACHE_KEY); } catch {}
20762099
} catch {}

0 commit comments

Comments
 (0)