Skip to content

An eager adoption pairs swapped or reordered raws across slots and aliases the two rows #4001

Open

Description

@ryansolid

What happens

An eager (unheld) adoption that receives the same two raws swapped or reordered pairs each slot's existing row with the other raw, and the two rows alias after the commit. On next @ 49a765c:

const A = { name: "a" };
const B = { name: "b" };
const [server, setServer] = createSignal({ x: A, y: B });
const [store] = createStore(() => server(), {});
createRenderEffect(createMemo(() => store.x.name + store.y.name), () => {});
flush();
// both rows are proxied now
setServer({ x: B, y: A });
flush();
[store.x.name, store.y.name]; // ["a", "b"] — unchanged; expected ["b", "a"] with two distinct proxies

The same holds for an unkeyed array reorder [A, B] to [B, A], and with only one of the rows proxied before the write.

Why

descend in reconcile.ts pairs slot x's target (A's) with B by position, which repoints the family lookup of B to that target; slot y then finds that target through B and pairs it with A again. A raw present in both frames moved, it did not change, and it should stay its own row. The held first-read pairing already follows that rule (#3986, ownRow in adoptChild).

What was tried on #3986

  • The rule on every eager pair (ownRow in descend): +5 B brotli, but a raw set per frame — CodSpeed measured -13% to -25% on the saturated reconcile benches.
  • The rule behind a once-per-frame scan for a proxied raw at a changed slot (hasRow): perf back within noise locally, +78 B brotli over the held-only form on createStore.

Both were reverted so the PR ships at +36 B. The tests for the eager cases are in the reverted commits (eae477f, d20ce53 on fix/adoption-lazy-child-pairing-3712): a record swap and an unkeyed reorder, held and unheld, plus the one-row-proxied swap and the immutable update kept at its slot.

Open question

An O(1) way to tell "this raw is a row of the family at another slot of the incoming frame" without scanning the frame or allocating. Positional identity (the slot keeps its proxy, the lookup of the moved-away raw is dropped) would be O(1) but is the opposite identity choice from the held path's rule.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions