Skip to content

fix(core): keep nodes a catch-up replay deletes and restores - #288

Closed
marcus-pousette wants to merge 3 commits into
mainfrom
fix/catch-up-touched-nodes
Closed

marcus-pousette wants to merge 3 commits into
mainfrom
fix/catch-up-touched-nodes

Conversation

@marcus-pousette

@marcus-pousette marcus-pousette commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

A late batch from a peer could leave a node deleted with an older payload, while a fresh replica of the same ops has it restored with the newer one. It happens when the replayed ops delete the node and then restore it, such as a delete followed by a peer's payload the delete didn't know about. The node stayed wrong until a later catch-up touched it.

catch_up_materialized_state rewrote only the nodes left in the coalesced replay changes, where that delete and restore cancel out, although the stored row was still deleted. Catch-up now rewrites every node a replayed op touched, plus their ancestors.

onMaterialized and changedSince missed the restore for the same reason. A new conformance scenario checks the node and both reports; it fails without the fix.

A catch-up after late ops replays the suffix from the frontier and
patched only the nodes left in the coalesced replay changes. A delete
and a later restore of the same node cancel out there, such as a peer's
payload the delete didn't know about. The node was skipped although its
stored row, which already reflects the old suffix, was still deleted
with an older payload. It stayed that way until a later catch-up
touched it, and neither onMaterialized nor changedSince reported it.

Catch-up now patches every node a replayed op touched, plus their
ancestors. The patch already reports a delete or restore against the
stored row, so the restore is reported too.
@marcus-pousette
marcus-pousette marked this pull request as ready for review November 2, 2026 13:59
The core unit test repeated the conformance scenario, which covers the
same catch-up through the SQLite and Postgres backends and fails on
main. The scenario also drops two ops it doesn't need: A's own delete
is in the replayed range, so it already forces the full replay.
marcus-pousette added a commit that referenced this pull request Nov 2, 2026
Taken from #288. A deletes a node, B writes a payload the delete didn't
know about, and B's ops reach A late. The catch-up here already repairs
that node and reports its restore, so #288's change to
materialization.rs isn't needed.

The changeset now names that fix too.
@marcus-pousette

Copy link
Copy Markdown
Collaborator Author

Closing in favor of #286, which includes this PR's conformance scenario and a broader version of the catch-up fix: it repairs every node the replayed ops touch, which also covers rows that change with nothing reported, such as a delete that leaves its node visible.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant