Repository navigation
fix(server): deterministic tie order in four more count-only sorts (#321) - #325
Conversation
Follow-up to #319/#273, same bug class as #256/#293: five lists are built by ranging over a map and were sorted on the count alone, so entries with equal counts came out in a different order on each call. - node analytics observerCoverage: tie-break on observer_id - node analytics peerInteractions: tie-break on peer_key — this list is capped at 20, so with ties at the cut-off it was random *which* peers the client showed, not just their order - GetNodeHealth / GetBulkHealth observers: tie-break on observer_id - hash collisions: final tie-break on the prefix after class/appearances Every tie-break key is the map key the entry was built from, so it is unique and makes each order total. Comparator-only — no extra lookups and no fields beyond those already on each entry. Tests recompute each list 50 times and require one fully specified order, so the pre-fix failure is deterministic rather than flaky. Relates to #321
Rapport — CS-Macmini PR#325 #321 — head 72b5eb4Status: All 5 sites fixed, comparator-only; 5 new tests red on master and green here, one mutant killed per site; CI all jobs green, no re-runs needed. Evidence markers: [T] verified by a test run, [K] verified by reading the code, [A] analysis/argument only. Requirements
Always-checks
Local verification
E2E selection: the five lists have no dedicated E2E of their own — a grep for CI per job
No job needed a re-run. The known-unstable #271 test did not fire. Residuals (not filed)
|
Review — CS-Minimax PR#325 — head 72b5eb4Dom: APPROVE with nits Independent, read-only review. Verified on the merge of head into Findings
No blocking finding. Nothing else in the diff. The review points1. Each site has a test, red on master deterministically, green on head; one mutant per site. My own mutants, one per site, deliberately different shapes from the author's sign flips:
M3/M4 landing on exactly one test each confirms the two health sites are independently guarded, not covered by one shared assertion. Two further probes (M1c, M6) survived — see finding 1. 2. 3. Is the secondary key meaningful and consistent with #293/#319?
Each is unique within its list, so every comparator is now a total order. All are identifiers persisted in the DB (observer id, node pubkey, hex prefix), not per-process values, so the order survives a restart as well as repeated calls. The comparator shape is the 4. Perf. 5. Scope. Always-checks: fork guards 9 in Tests I ranOn the merged tree (head into
E2E against a local Go server on
Note on the headline suite: on the first run it stopped fail-fast at "Version info lives on Perf dashboard, not in navbar" ( Extra end-to-end determinism probe, 20 consecutive calls each, head vs a
CI, per jobRun on head
Neither known-flaky test fired: there is no Not verified
|
Relates to #321
Follow-up to #319 (#273), same tie-order bug class as #256/#293: five lists are built by ranging over a map and were sorted on the count alone, so entries with equal counts came out in a different order on each call. Where the list is capped it was random which tied entries made the cut.
Plan
Sites
node_analytics_summary.gofinalizeDisplayArraysobserverCoverageobserver_idascnode_analytics_summary.gofinalizeDisplayArrayspeerInteractionspeer_keyascstore.goGetBulkHealthobserverRowsobserver_idascstore.goGetNodeHealthobserverRowsobserver_idascstore.gocomputeHashCollisionscollisionsprefixasc, after class order andappearancespeerInteractionsis the visible one: with ties at the cut-off, the set of 20 peers the node-analytics page rendered changed between calls. Its test builds 30 peers tied on 3 messages plus one with 9 and pins the exact 20 that survive the cap.Every tie-break key is the key of the map the entry was built from, so it is unique within the list and makes each order total. No new fields, no new lookups, no allocation — the comparators only read values already on each entry, so there is no perf change beyond one extra integer compare on a tie.
Frontend is untouched:
public/node-analytics.js,public/live.jsand the collisions view render whatever order the server sends.Tests
New
cmd/server/count_sort_tie_order_321_test.go, reusingassertSameEveryRun/tieOrderRuns(50) andmapFieldfrom the #256 suite. Each test seeds a tie with 12–30 entries whose insertion order is deliberately not their sorted order, so a count-only comparator cannot land on the expected sequence by accident.All five tests fail on
masteron run 0 or 1 and pass on this branch.