GREEN for the test added in the previous commit. Root cause:
cmd/server/store.go Load() (~783-799) per-observation unmarshals
o.resolved_path, extracts every relay-hop pubkey, and feeds them
to addToByNode + addToResolvedPubkeyIndex +
addResolvedPubkeysToPathHopIndex. loadChunk (~937-1023) — the
background-backfill path that loads everything past
hotStartupHours — scans the column into resolvedPathStr but never
unmarshals it. Result: a contract violation between Load and
loadChunk: same SQL rows, two different post-conditions. After
every container restart, transmissions older than hotStartupHours
are present in s.packets / s.byHash / s.byTxID but missing from
s.byNode[relayPK] for every relay pubkey, collapsing Home-page
per-node packetsToday / totalTransmissions / observers /
avgHops / avgSnr for relay-heavy nodes (e.g. 753 → 8 in the
reporter's reproduction). Stats only self-heal as live ingest
re-populates byNode through the ingest path.
Fix shape:
1. Extract a single (s *PacketStore) indexResolvedPathHops helper
that owns the addToByNode + addResolvedPubkeysToPathHopIndex +
addToResolvedPubkeyIndex sequence. This collapses three inline
duplications across Load, the MQTT ingest path (~2293-2306),
and the late-arriving-observation path (~2630-2643) into one
point of truth so the invariant is structural, not duplicated.
2. In loadChunk, unmarshal resolved_path per row OUTSIDE the merge
critical section, dedupe relay pubkeys per txID into
localResolvedPKsByTx, and feed them through indexResolvedPathHops
inside the existing per-batch merge lock alongside indexByNode.
This matches loadChunk's 'build local, merge under lock' shape
— the lock-held work stays bounded (no JSON unmarshal under
s.mu) per AGENTS.md performance rule '#0: no expensive work
under locks'.
3. Re-point Load and both ingest sites at the helper. Load's
semantic behaviour is byte-identical (helper does exactly what
the inlined block did).
Test:
go test -run TestLoadChunk_IndexesResolvedPathPubkeys_Issue1558 ./cmd/server/...
→ PASS
Full suite: cmd/server go test ./... → PASS.
Companion issue #1546 covers the sticky /api/stats backfilling=true
flag mentioned in the reporter's writeup; not fixed here.
Closes#1558