fix(signals): preserve transactions reentered during finalization - #3319
fix(signals): preserve transactions reentered during finalization#3319DerpyCrabs wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 790f180 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 |
…cts follow ownership (#3319) An ambient flush can enter a held transaction while finalizing (store commit hook, boundary check, or a stamped recompute) and then kept committing state and applying effects as though it still owned the batch, leaving the UI stale once the transaction settled. finalizePureQueue captures the batch it started with and settles nothing an entered transaction adopted; a completing transaction whose ambient batch was separate still settles its own containers. Effects follow the #3322 owner stamp: in a flush whose finalize entered a transaction, runEffect leaves runs owned by a still-held transaction queued for the next gate to park, and applies everything computed mainline — the write that caused the flush reads and renders together. Lanes are exempt by construction (they never enter the ordinary queue). The finalize guard and the two regression tests come from PR #3319 by DerpyCrabs; the whole-flush park it proposed is replaced with per-effect ownership, which the two added tests distinguish. Closes #3319 Co-authored-by: DerpyCrabs <derpycrabs@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
…core floor) runEffect reads activeTransition directly — the #3319 flag was set from the same test at the same point in flush(). The lane exemption moves to where lanes live: optimistic.ts ORs LANE_RUN into the run type, as does effect()'s creation-time immediate run. contestEffect (#3322) inlined into its single call site in recompute. Behavior-identical at every runEffect entry point. Core floor 21,931 -> 21,890 minified; gzip -38 B core, -48 B isPending/latest. Co-authored-by: Claude via Cursor <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
An ambient flush can re-enter a pending transaction while finalizing, then continue committing state and applying effects as though it still owns the original batch.
We hit this in an application when an SSE reconnect triggered parallel query refetches. One refetch updated a settings projection read through
snapshot(deep(settings.pins)), while another refetch was still pending. An unrelated signal update triggered a flush; committing the settings store notified its deep reader and re-entered the pending transaction. Once both refetches completed, the network data was current but the pins UI remained stale.finalizePureQueue()can adopt another transaction through store commit hooks, boundary checks, or recomputations. Two parts of finalization currently assume that has not happened:This patch captures the batch identity before the first
commitPendingNodes()and skips the remaining commit/reversion work if that identity changes. It also returns through the transition gate before applying effects when finalization leaves an active transition.The regressions cover both entry points:
Both tests fail on current
nextand pass with this patch. The full repository tests and type checks, compiler suite, package integration checks, and TanStack Solid Query suite also pass.