docs(bf-4nybh): confirm Mode A partitioning gaps a/b/c as genuine
Independent re-verification of the three candidate coverage gaps. All three
CONFIRMED:
(a) survivor-stability/minimal-reshuffling not pinned in test_mode_a_pod_reassignment
(b) no Mode A scale-up 3->4 (the p4_topology_chaos 3->4 test is a separate
Rebalancer/Topology subsystem using router::score, a different hash
construction than rendezvous_score, and never touches ModeACoordinator)
(c) no pinned shard->owner golden vector; owner_for_shard is never called by
any test, only the boolean owns_shard is asserted
Genuine gaps = {a, b, c}.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a05055b8ad
commit
fb5eeec7f9
1 changed files with 121 additions and 0 deletions
121
notes/bf-4nybh.md
Normal file
121
notes/bf-4nybh.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# bf-4nybh — Confirm-or-refute the Mode A partitioning test gaps (step 3)
|
||||
|
||||
Investigation step 3 of umbrella **bf-1zn8a**. Independent re-verification of the
|
||||
three candidate coverage gaps (a/b/c) so later siblings attack only REAL holes.
|
||||
|
||||
## TL;DR — all three CONFIRMED (genuine gaps)
|
||||
|
||||
| # | Candidate gap | Verdict | Key evidence |
|
||||
|---|---|---|---|
|
||||
| (a) | survivor-stability / minimal-reshuffling not pinned | **CONFIRMED** | `anti_entropy.rs:1665` records only `pod3_owned_initial`; never asserts survivors keep their shards |
|
||||
| (b) | no scale-UP (3→4); only scale-DOWN (3→2) | **CONFIRMED** | only Mode A resize is 3→2 (`anti_entropy.rs:1714`); the one 3→4 test is a *different subsystem* |
|
||||
| (c) | no pinned shard→owner golden vector vs `top1_by_score(hash(s‖pid))` | **CONFIRMED** | `owner_for_shard` is never called by any test; only boolean `owns_shard` is asserted |
|
||||
|
||||
**GENUINE gaps = { (a), (b), (c) }** — all three. Nothing was refuted as
|
||||
already-covered.
|
||||
|
||||
---
|
||||
|
||||
## Verdict (a) — CONFIRMED: survivor-stability / minimal-reshuffling
|
||||
|
||||
`test_mode_a_pod_reassignment` (`crates/miroir-core/src/anti_entropy.rs:1665`)
|
||||
goes 3→2 (kill pod-3) and asserts two things about pod-3's *old* shards:
|
||||
|
||||
- each previously-pod-3 shard is now owned by exactly one of pod-1/pod-2 —
|
||||
`owner_count == 1`, `anti_entropy.rs:1731-1737`
|
||||
- all 64 shards are still owned — `total_owned == 64`, `anti_entropy.rs:1751-1755`
|
||||
|
||||
What it records initially is **only** `pod3_owned_initial`
|
||||
(`anti_entropy.rs:1704-1711`). It never records pod-1's or pod-2's pre-removal
|
||||
shards and never asserts they are unchanged afterward. A buggy implementation
|
||||
that reshuffled **every** shard on each peer-set change — while still preserving
|
||||
exactly-one-owner + full coverage — would pass this test.
|
||||
|
||||
The "minimal reshuffling" property is an explicit Mode A design invariant:
|
||||
`mode_a_coordinator.rs:19` ("ownership with minimal reshuffling") and
|
||||
`plan.md` §14.5 (cited at `mode_a_coordinator.rs:1`). Un-pinned for the
|
||||
background-work ownership path. **CONFIRMED.**
|
||||
|
||||
## Verdict (b) — CONFIRMED: no Mode A scale-up
|
||||
|
||||
The only Mode A peer-set *mutation* anywhere is the 3→2 downscale
|
||||
(`anti_entropy.rs:1714`, `peer_set_2pods`). A whole-crate grep for `"pod-4"` /
|
||||
`peer_set_4pods` / `4pod` returns **nothing** — no Mode A test adds a 4th pod.
|
||||
|
||||
One apparent 3→4 scale-up test exists —
|
||||
`p4_topology_chaos.rs:282` (`chaos_rebalance_optimal_movement`, adds `node-3`) —
|
||||
but it does **not** cover Mode A and therefore does not refute the gap:
|
||||
|
||||
- It runs in the **Rebalancer / Topology** subsystem, which never references
|
||||
`ModeACoordinator` / `owner_for_shard` / `rendezvous_score`
|
||||
(`grep` of `rebalancer.rs` / `topology.rs` for those symbols → empty).
|
||||
- It routes through `assign_shard_in_group` → router's `score()`
|
||||
(`router.rs:25`, `router.rs:12`), which is a **different hash construction**
|
||||
from Mode A's `rendezvous_score`:
|
||||
- router `score(shard_id: u32, node_id: &str)`: `XxHash64::with_seed(0)` →
|
||||
`shard_id.hash(h)` (raw `u32` bytes) → `node_id.hash(h)` (std `str` Hash,
|
||||
appends a `0xff` terminator). No separator.
|
||||
- Mode A `rendezvous_score(shard_id: &str, peer_id: &str)`
|
||||
(`mode_a_coordinator.rs:112`): `XxHash64::with_seed(0)` →
|
||||
`write(shard_id.as_bytes())` (decimal string, e.g. `"42"`) → `write(b"||")`
|
||||
→ `write(peer_id.as_bytes())`. No `0xff` terminator.
|
||||
|
||||
Different input encoding, different separator, different terminator ⇒ different
|
||||
scores, different code path. The p4 test validates the rebalancer's migration
|
||||
accounting, not Mode A rendezvous partitioning. **Mode A scale-up is genuinely
|
||||
untested. CONFIRMED.**
|
||||
|
||||
## Verdict (c) — CONFIRMED: no pinned shard→owner golden vector
|
||||
|
||||
`owner_for_shard` (`mode_a_coordinator.rs:123`) is the only API that returns a
|
||||
*concrete* owner, yet it has exactly **one** call site — internal, inside
|
||||
`owns_shard` (`mode_a_coordinator.rs:156`). No test calls it
|
||||
(`grep -rn "owner_for_shard" --include=*.rs crates/ | grep -v fn` → only `:156`).
|
||||
|
||||
Every Mode A ownership test instead calls the boolean `owns_shard` and asserts
|
||||
only *properties*:
|
||||
|
||||
- `test_owns_exactly_one_peer_per_item` (`mode_a_coordinator.rs:422`): exactly
|
||||
one pod owns each item — never *which* pod.
|
||||
- `test_mode_a_three_pods_each_shard_processed_once`
|
||||
(`anti_entropy.rs:1541`): exactly-one-owner + full coverage + even spread.
|
||||
- `test_rendezvous_score_*` (`mode_a_coordinator.rs:317/325/333`): determinism
|
||||
+ peer-sensitivity + shard-sensitivity of the raw score — never a pinned
|
||||
owner.
|
||||
- `tests/hash_fixtures.rs` only `println!`s (no `assert!`) and covers the
|
||||
router's key→shard *modulo* mapping, not Mode A ownership.
|
||||
|
||||
So no test pins a specific shard→owner mapping against the documented
|
||||
`top1_by_score(hash(shard_id‖peer_id))` rule (`mode_a_coordinator.rs:112-118`).
|
||||
A change to the hash construction that preserves those properties while silently
|
||||
reassigning all ownership — e.g. swapping the `"||"` separator
|
||||
(`mode_a_coordinator.rs:115`) to `"|"`, swapping the shard/peer write order, or
|
||||
reseeding `XxHash64` (`:113`) — would pass every existing test yet break the
|
||||
documented `hash(s‖pid)` contract. A golden-vector test (fixed `peer_set` +
|
||||
hardcoded expected owner per shard) is the missing guard. **CONFIRMED.**
|
||||
|
||||
---
|
||||
|
||||
## Corroboration note
|
||||
|
||||
These verdicts independently reproduce the gap analysis already recorded in the
|
||||
sibling note `notes/bf-1zn8a.md` (step 3, lines 153-208). This bead's added
|
||||
value is the explicit, cited refutation-of-the-refutation for (b): the
|
||||
`p4_topology_chaos` 3→4 test looks like it could cover scale-up, but it targets
|
||||
the separate Rebalancer/Topology subsystem with router's `score()` — a different
|
||||
hash construction — so it does **not** close the Mode A gap.
|
||||
|
||||
## Acceptance criteria (bf-4nybh)
|
||||
|
||||
- [x] Verdict for (a) minimal-reshuffling: **CONFIRMED** —
|
||||
`anti_entropy.rs:1665,1704-1711,1731-1737,1751-1755`; property at
|
||||
`mode_a_coordinator.rs:19`.
|
||||
- [x] Verdict for (b) scale-up: **CONFIRMED** — only Mode A resize is 3→2
|
||||
(`anti_entropy.rs:1714`); no `pod-4` crate-wide; the p4 3→4 test
|
||||
(`p4_topology_chaos.rs:282`) uses router `score()` (`router.rs:12`) ≠
|
||||
`rendezvous_score` (`mode_a_coordinator.rs:112`) and never touches
|
||||
`ModeACoordinator`.
|
||||
- [x] Verdict for (c) pinned hash vector: **CONFIRMED** — `owner_for_shard`
|
||||
(`mode_a_coordinator.rs:123`) never called by tests; only boolean
|
||||
`owns_shard` asserted; rule at `:112-118`.
|
||||
- [x] Final GENUINE gaps = { (a), (b), (c) }.
|
||||
Loading…
Add table
Reference in a new issue