fix(signals): two flights through one memo settle as one unit; lane-dirtied zombies run (#3443, #3444) - #3450
Conversation
🦋 Changeset detectedLatest commit: aa2cfcc The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
…irtied zombies run (#3443, #3444) flight, entangles the two at the propagation. The second flight only propagated pending onto the memo (no recompute — its inputs' values were unchanged), so the memo's stamped re-entry never ran and the first transaction never learned it was waiting on the second: it revealed `A: 1` beside the committed `Sum: 0`, and `Sum: 2` arrived with `B: 1`. `notifyStatus`'s dependent walk now enters the held memo's transaction (A15: a shared derivation of both). Effects are skipped — an effect entangles nothing by itself (A15 shared-hole corollary). The write that starts the second flight is held with the first when its async work flows into a held memo; the #3375 boundary-reset pin publishes `Sum: 2` once at the reset instead of a two-pass `Sum: 1 | Sum: 2`. zombie recomputes its staged writes queued (a world the zombie never displays). A zombie dirtied through the lane channel (OPTIMISTIC_DIRTY — an override or a `latest()` companion) displays exactly that world mainline until the commit disposes it, so it runs instead: `latest(count)` inside a branch a held `Show` is removing now follows the value outside. Docs: A15 mechanism completed (#3443) and pinned by tests/overlapping-flights.test.ts; INTERNALS bullets for both; rules index regenerated. Floor 23,365 -> 23,419 (+54 B, conscious bump to 23,450); three brotli caps ratcheted 0.05 KB with notes. Co-authored-by: Claude via Cursor <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
a056d36 to
d89a3c8
Compare
Merging this PR will degrade performance by 10.67%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | projection derive: write one NESTED field (reference) |
2.5 ms | 2.8 ms | -10.67% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing fix/async-memo-overlap (aa2cfcc) with next (75c5113)
…3443 carve-out) A memo stamped by another live transaction but holding nothing of it — status clear, nothing staged — is not entangled when a second flight propagates pending onto it. The stamp alone decides nothing (#3334): a Dynamic switched twice mid-flight dragged the superseded first call's gate into the live second call's reveal (web call-driven-lifecycle args-switch-gate: expected Two, got Zero). Keyed on STATUS_PENDING or a staged _pendingValue instead; #3443's shapes are unchanged. Co-authored-by: Claude via Cursor <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Coverage Report for CI Build 34938687153Coverage remained the same at 71.842%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Measured at 8718 B against next's 8691 (+27). The stamp-only first cut fit under 8.70 at 8690; keying entanglement on STATUS_PENDING / a staged value is what tips the cap. Every other scenario unchanged and within cap. Co-authored-by: Claude via Cursor <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
Fixes #3443. Fixes #3444.
#3443 — a shared memo disagrees with its async inputs during overlapping updates
Before:
3000: A: 1alone,3500: B: 1 | Sum: 2. Traced with the reporter map:setBopens T3 andb's pending propagates ontosum— a memo T2 holds — without recomputing it (its inputs' values are unchanged), so the memo's stamped re-entry never runs and T2 never learns it is waiting onb.sum's reader registers onbin T3 (INV-3 is keyed by transaction); T2's verdict ata's landing sees only its own reporters, commits, and revealsA: 1beside the committedSum: 0.Fix:
notifyStatus's dependent walk, on a pending propagation reaching a memo stamped by another live transaction, enters that transaction (initTransition(sub._transition)— a merge, or with nothing active an entry that adopts the ambient batch). This is A15's headline case (a shared derivation of both flights settles as one unit) applied at the one moment it is known. Effects are skipped: an effect entangles nothing by itself (A15 shared-hole corollary, #3407) — the plain-getter shape stays parallel (3000: A: 1 | Sum: 1,3500: B: 1 | Sum: 2), pinned alongside.Result:
3500: A: 1 | B: 1 | Sum: 2.Semantic to note: the write that starts the second flight is held with the first when its async work flows into a held memo —
page=1waits withcount=1whiledetailsre-asks, even thoughSumreadspageplainly. Pinned. The existing #3375 boundary-reset pin moves accordingly: the reset now publishesSum: 2once instead of the two-passSum: 1 | Sum: 2its comment described as a drain artifact; every other frame in that pin is unchanged.#3444 —
latest()inside aShowstays stale while removal is pendingWhen the parking batch is the transaction,
flushcancels the zombie recomputes its staged writes queued (cancelZombieRecompute) — a zombie renders mainline until the commit disposes it, and the staged world is one it never displays. A zombie dirtied through the lane channel (REACTIVE_OPTIMISTIC_DIRTY: an override, or thelatest()companion the write synced) is the exception: the lane's values are the mainline frame, so the still-visible branch showedlatest(count)at 0 beside the same read outside at 1. Such a zombie now runs; its pass runs under the lane and its run lands on the lane's effect queue, so a held lane defers it as it defers every other reader's.Result:
500: Inside: 1 | Outside: 1,1500: Inside gone.Verification
@solidjs/signals: 173 files / 1804 tests green on top of fix(signals): a memo computes under its own lane posture, never its puller's (#3442) #3445. Newtests/overlapping-flights.test.ts(Memoized sum disagrees with its async inputs during overlapping updates #3443 shape, the effect-arm control, the held-second-write consequence) and a latest inside Show stays stale while removal is pending #3444 pin beside 2.0.0-rc nested render effect cleanup run too early #3404 intests/nested-render-effect-async-cleanup.test.ts.--checkclean). Changeset included.+ createStore15679,isPending/latest11066, observe tier 15681); every other scenario within cap.