fix: un State tardif EST la barriere, donc il est enregistre quand il arrive
En relisant ce que je venais d'ecrire dans le contrat - « si le document se synchronise plus tard et apporte des donnees, la lecture repond d'elle-meme » - j'ai constate que c'etait faux. `onState` sortait aussitot des lors que le repli borne avait deja termine l'ouverture, donc un `State` tardif etait jete : `"timed-out"` restait indefiniment sur un depot desormais synchronise, et le lecteur qui consulte cet etat ne pouvait jamais l'apprendre. Le repli veut dire « cette ouverture a cesse d'attendre », jamais « la barriere ne sera pas franchie » : l'abonnement n'est pas ferme, donc le `State` arrive encore. `onState` enregistre desormais `"synced"` avant le controle. `"timed-out"` -> `"synced"` est la seule transition ajoutee, et elle va de moins d'autorite vers plus ; les deux lecteurs de cette valeur la veulent ainsi. (dd) l'epingle : elle tombe si l'enregistrement repasse sous le controle.
This commit is contained in:
@@ -307,9 +307,17 @@ export async function openRepoUnguarded(nuri: Nuri): Promise<void> {
|
||||
});
|
||||
// Resolve on the first `State` (the sync BARRIER), marking the nuri "synced".
|
||||
const onState = (): void => {
|
||||
// The first `State` IS the barrier, WHENEVER it arrives. Recorded before the
|
||||
// already-settled check on purpose: the bounded fallback below only means "this
|
||||
// open stopped waiting", never "the barrier will not be crossed" - the
|
||||
// subscription is still held, so a `State` can still land afterwards. It used to
|
||||
// be dropped, which left `timed-out` standing for ever on a repo that had since
|
||||
// synced, and a reader consulting that state (`surface/use-shape-query.ts`) could
|
||||
// never learn better. `timed-out` -> `synced` is the only transition this adds,
|
||||
// and it is the one that goes from less authority to more.
|
||||
syncState.set(nuri, "synced");
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
syncState.set(nuri, "synced");
|
||||
logStage("BARRIER " + shortNuri(nuri) + " synced (" + (Date.now() - barrierStartedAt) + "ms)");
|
||||
resolve();
|
||||
};
|
||||
|
||||
@@ -185,6 +185,12 @@ function makeFake(options: { pushState?: boolean; subscribe?: boolean } = {}) {
|
||||
// is withheld in `inject` rather than here, so the two suites that count subscribes
|
||||
// keep a mock to count.
|
||||
_noSubscribe: options.subscribe === false,
|
||||
/** The broker catching up: push the first `State` to a subscription already open on
|
||||
* `nuri`, late. A subscription is never dropped by the bounded fallback, so this is
|
||||
* what a slow repo really does. */
|
||||
_pushStateNow(nuri: string): void {
|
||||
for (const sub of subs) if (sub.nuri === nuri) sub.cb({ V0: { State: {} } });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1135,6 +1141,33 @@ describe("useShapeQuery", () => {
|
||||
unsub();
|
||||
});
|
||||
|
||||
it("(dd) a `State` that lands AFTER the fallback answers the read, with no call from the app", async () => {
|
||||
const ng = makeFake({ pushState: false });
|
||||
const orm = makeOrm();
|
||||
inject(ng, orm);
|
||||
setOpenTimeoutForTests(30);
|
||||
setCurrentUser("uma");
|
||||
const mine = await createEntityDoc("uma", "protected");
|
||||
|
||||
const obs = useShapeQuery(EventShape, { graphs: [mine] });
|
||||
const unsub = obs.subscribe(() => {});
|
||||
for (let i = 0; i < 40 && getSyncState(mine) !== "timed-out"; i++) await tick();
|
||||
expect(getSyncState(mine)).toBe("timed-out");
|
||||
expect(obs.getSnapshot().isSuccess).toBe(false); // no authority yet, so no answer
|
||||
|
||||
// The broker catches up: the subscription was never dropped, so the first `State`
|
||||
// still lands - late, but it IS the barrier - and it brings the document's data, which
|
||||
// is what makes the ORM republish (`orm-fake.seed` pushes once delivery has happened,
|
||||
// as an engine patch reaching the signal object does). Nothing here calls `refetch`.
|
||||
ng._pushStateNow(mine);
|
||||
seedEvent(orm, mine, `${mine}:q:one`, "Late");
|
||||
for (let i = 0; i < 40 && !obs.getSnapshot().isSuccess; i++) await tick();
|
||||
|
||||
expect(getSyncState(mine)).toBe("synced");
|
||||
expect(titles(obs.getSnapshot().data)).toEqual(["Late"]);
|
||||
unsub();
|
||||
});
|
||||
|
||||
// -- teardown ------------------------------------------------------------
|
||||
|
||||
it("(k) the last unsubscribe closes the shape query", async () => {
|
||||
|
||||
Reference in New Issue
Block a user