Conversation
🦋 Changeset detectedLatest commit: 586b5f3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 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 |
Merging this PR will improve performance by 8.03%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | memo + sync render effect only (reference) |
29.3 ms | 27.2 ms | +8.03% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing brenelz:fix/ispending-memo-gate-3766 (586b5f3) with next (a8c98bd)
|
…js#3766) A render effect that read a memo over isPending(source) and then went pending on a second async memo became the verdict lane's work. The landing re-ran it outside the lane, where the uninitialized probe memo had nothing to show, so it threw with no source to wake it and the lane stayed blocked on its own reader. Only a leaf's own lane pass that ends pending is marked; a pending propagation or a first pass under a lane is not. A held render effect that a lane took over and whose pass leaves the lane goes back to the lane's holder instead of keeping CONFIG_HELD with no transaction, which crashed in txOf under the semantic fuzzer. This also fixes F5, so its pin is now a plain test.
a887ad0 to
586b5f3
Compare
Summary
Refs #3766. Since #3774, a render effect that reads a memo over
isPending(source)and then a second, initially async memo never mounts (the reduced repro in this comment,`${pending()} | ${gate()}`). Both promises fulfill and nothing updates afterwards.Root cause: when
sourcelands, thependingmemo re-runs as a node of the transaction's verdict lane while still uninitialized on screen. The effect reads it, becomes the lane's work, and goes pending ongate, so the lane is blocked on its own member. Whengatelands the effect re-runs outside the lane, because only non-leaf nodes carryCONFIG_OVERRIDEto seat their pass there.laneReadthen sees a held, unshown, uninitialized node and throwsNotReadyError(null). No source wakes the effect again, the lane never shows, and the transaction never lands.The change is one branch in
laneStage(lanes.ts): a leaf that ends a pass pending as a lane's work is flaggedREACTIVE_LANE_DIRTY, so its next pass is seated in that lane, the same way a lane's derivation is. I first put this insettlePendingSource, scoped to the landing only, but that added 31 minified bytes to the signals core floor;lanes.tsis pay-for-use and the core floor is byte-identical here.With the mount fixed, the original playground from the issue no longer shows two different
gate()values: both rows go fromfalse | 100tofalse | 200in the same flush.How did you test this change?
packages/signals/tests/ispending-memo-gate-3766.test.ts: the three read orders from the comment, a second reader still waiting on a slower flight, a plain write while the reader waits, and the original update scenario polled for disagreement.packages/web/test/ispending-memo-gate-3766.spec.tsx: the playground through compiled JSX. Both tests fail onnextat 6f77b1bd9 and pass with the change.npx vitest runinpackages/signals: 4929 passed, 0 failed.packages/solid: 819 passed.npx vitest runinpackages/web: 1135 passed, 6 failed. The same 6 (dev-warning,lowercase-on-attribute,performance-tracks) fail onnextwithout this change on my machine, which runs an older prebuilt compiler binary.node size.mjsinscripts/sizepasses with the caps below.Open
Size caps need a maintainer decision. Five brotli caps are raised, two of them frozen floor caps, so
check-floor-capsfails until a size exception line is added to this description. I have not added one because the cost has not been accepted.nextThe three scenarios with no minified change do not include the edit; the added
_flagsuse shifts the property mangle order and the growth is brotli layout.The flag also covers a leaf that ends a lane pass errored, and it stays set until the leaf's next pass, including across the lane dissolving. I did not find a failing case for either, but a narrower marker would cost core bytes, so I left it for review.
🤖 Generated with Claude Code