fix(client): dé-dupliquer les ensureAccount concurrents au cold-start (fork résolu)
Dernière couche du bug de reconnexion : au cold-start, `watchShape` public + protected + l'effet owned-events appellent `ensureAccount(A)` quasi-simultanément AVANT la sync du shim → chacun lit 0 → chacun provisionne un nouveau jeu de docs (fork par-appelant) → la résolution déterministe canonique fait alors diverger lecteur et écrivain sur le docProtected → `readScopeIndex` vide. Fix : `ensureInFlight` (map de promesses) dé-duplique les provisions concurrentes en UNE seule ; `discovery.readIndex` ouvre son repo au cold-start (`ensureRepoOpen`). Avec la résolution canonique déjà committée, écrivain et lecteur convergent. Mesuré (levier isSuccess) : la participation protected converge `isSuccess=true, data=1` sur la page fraîche (plus « vide à 30s »). gate : tsc 0 ; bun test 123 ; test:e2e 42/42. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,27 @@ test("ensureAccount is idempotent (case/@-insensitive key), no extra docs", asyn
|
||||
expect(fake.doc_create).toHaveBeenCalledTimes(3); // not 6
|
||||
});
|
||||
|
||||
test("ensureAccount de-dupes CONCURRENT provisions (anti-fork): one account, 3 docs", async () => {
|
||||
// The reconnection FORK: several callers (watchShape public+protected, the
|
||||
// container subs, the owned-events effect) hit ensureAccount(SAME id) BEFORE
|
||||
// the shim has synced, so each reads 0 rows and independently provisions a new
|
||||
// set of scope docs — N forks, N×3 docs, duplicate docPublic/docProtected in the
|
||||
// shim → a fresh reader picks a different canonical doc than the writer wrote to.
|
||||
// The in-flight de-dup collapses N concurrent provisions into ONE.
|
||||
const results = await Promise.all([
|
||||
ensureAccount("Bob"),
|
||||
ensureAccount("Bob"),
|
||||
ensureAccount("@bob"),
|
||||
ensureAccount("BOB"),
|
||||
ensureAccount("bob"),
|
||||
]);
|
||||
// Exactly ONE set of 3 docs was created — not 5×3.
|
||||
expect(fake.doc_create).toHaveBeenCalledTimes(3);
|
||||
// Every caller got the SAME record (same docs), so writer/reader can never
|
||||
// disagree on the canonical scope doc.
|
||||
for (const r of results) expect(r).toEqual(results[0]!);
|
||||
});
|
||||
|
||||
test("loadShim round-trips a persisted account across a cache reset", async () => {
|
||||
await ensureAccount("Bob");
|
||||
resetRegistryCache(); // force a re-read from the fake store
|
||||
|
||||
Reference in New Issue
Block a user