fix(registry): converge the identity-index repair pass on twin stores
Drive the RED twin-store pin green: the repair walk now collects its identity->content mappings into a local last-wins map and appends after the walk only the entries whose final walk value differs from the pre-walk snapshot (collect-then-diff-append, decision on #191). The old per-entry comparison against the mid-walk-stale snapshot appended one alternating line per scan-triggering lookup while same-identity twins coexisted — the sidecar now converges to zero growth there too. Lookup answers (first read_dir match) and verify-on-hit untouched. Verification: workspace suite 1347 passed / 0 failed; clippy -D warnings clean. refs #191
This commit is contained in:
@@ -455,6 +455,17 @@ impl Registry {
|
||||
Err(_) => return Ok(None), // no store yet -> nothing matches
|
||||
};
|
||||
let mut found = None;
|
||||
// Collect-then-diff-append (#191): the walk's last-wins mapping is
|
||||
// built locally first, and only entries whose FINAL value differs
|
||||
// from the pre-walk `index` snapshot are appended, once, after the
|
||||
// walk. This is what makes a repeated scan over an already-converged
|
||||
// index a no-op even in the duplicate-identity/distinct-content twin
|
||||
// corner (two stored blueprints sharing an identity id, e.g.
|
||||
// differing only in debug names): comparing each entry against the
|
||||
// stale pre-walk snapshot mid-walk would append one alternating line
|
||||
// per twin every time, since the snapshot never equals both of them
|
||||
// at once.
|
||||
let mut walked = std::collections::BTreeMap::new();
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let json = fs::read_to_string(entry.path())?;
|
||||
@@ -467,23 +478,18 @@ impl Registry {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if index.get(&entry_identity) != Some(&content_id) {
|
||||
// absent-or-different only, so a walk over an already-correct
|
||||
// index appends nothing — *for the common one-content-per-
|
||||
// identity case*. In the duplicate-identity/distinct-content
|
||||
// corner (two stored blueprints sharing an identity id, e.g.
|
||||
// differing only in debug names), `index` is this call's
|
||||
// snapshot and is never updated mid-walk, so every entry
|
||||
// whose content id differs from that stale snapshot appends
|
||||
// a line, one per full-scan lookup, even though each write is
|
||||
// individually a valid mapping. Best-effort regardless — a
|
||||
// cache write must never degrade a correct read.
|
||||
let _ = self.append_identity_index(&entry_identity, &content_id);
|
||||
}
|
||||
walked.insert(entry_identity.clone(), content_id);
|
||||
if found.is_none() && entry_identity == identity_id {
|
||||
found = Some(json);
|
||||
}
|
||||
}
|
||||
for (walked_identity, content_id) in &walked {
|
||||
if index.get(walked_identity) != Some(content_id) {
|
||||
// Best-effort — a cache write must never degrade a correct
|
||||
// read.
|
||||
let _ = self.append_identity_index(walked_identity, content_id);
|
||||
}
|
||||
}
|
||||
Ok(found)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user