Repository navigation
fix(T13868): concurrent first docs updates no longer wedge the second device's stream - #2359
Merged
Merged
Conversation
… device's stream Rewinding a local txn undid its conduit_attachments INSERT while its append-only conduit_attachment_versions children still pointed at it, so the immediate FK check refused and every later cloud sync failed with a bare 'FOREIGN KEY constraint failed'. Rewind, apply and replay share one frame transaction, so the rewind now defers FKs to COMMIT; the parent is back (stream or replay) before the check. conduit_attachment_versions / _approvals declared their uid columns as refs, which translate through the target's LOCAL key (slug, id): the sealer found no row and sent NULL, so peers stored versions with no doc and no parent. Those columns already hold uids; they now travel verbatim like conduit_doc_heads. A deferred FK that still fails at COMMIT now names the child row and its parent table (E_SYNC_APPLY_FK). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Task: T13868 (critical, under T12323). Found by the journal gate on #2254's build (S8).
Root cause
Each device makes its first
docs updateof the same synced doc concurrently. Each one lazily mints its ownconduit_attachmentsrow for the doc (T13734), withconduit_attachment_versionschildren and aconduit_doc_headsrow. Two defects combine:conduit_attachmentsINSERT but skips append-only children. Its ownconduit_attachment_versionsrows still point at the doc, so the immediate FK check refuses the delete. The frame then rolls back, and every latercloud syncfails with a bare "FOREIGN KEY constraint failed". The peer's segment stays staged.conduit_attachment_versionsandconduit_attachment_approvalsdeclaredattachment_uid,parent_version_uidandversion_uidas refs. A ref is translated through the target's LOCAL key (slug,id), but these columns already hold uids. The sealer found no row and sent NULL, so peers stored versions with no doc and no parent.Fix
apply/applier.tsrewindTxns: setsPRAGMA defer_foreign_keys = ON. Rewind, apply and replay share the frame's transaction, so the parent is back (from the stream or the replay) before COMMIT checks. The applier already does this for a display-key move (applyOne,write-api.ts).row-identity-registry.ts: the two tables drop thoserefs. The columns travel verbatim, likeconduit_doc_headsand the slug aliases already do.apply/frame.ts: a deferred FK that still fails at COMMIT throwsE_SYNC_APPLY_FKnaming the child uid and its parent table (AC3, "or the error names the row"). The frame still rolls back whole.Residual (reviewed, accepted for this fix)
Once a page rebases, FKs stay deferred for the rest of that frame, the same as after a key move today. If an incoming op in that page has a dangling FK, it no longer voids as a per-op
guardconflict. The frame instead fails at COMMIT, now with the rows named. The applier's own dangling-ref detection runs before the write, so the FK is a backstop. A follow-up could restore immediate checks after the replay.Tests
two-device-e2e.test.ts: concurrent firstupdateDocBySlugon both devices, in both pull orders. Neither device has anything left staged,foreign_key_checkis clean, there is oneconduit_attachmentsrow, and docs, versions and heads are identical on both devices.apply-frame.test.ts: a deferred FK refused at COMMIT namesconduit_doc_slug_aliases <uid> → conduit_attachments, rolls back, and leaves no transaction open.apply-frame.test.tscase and the neighbouring test files never ran (they were queued when the directive landed). GitHub CI's Unit Tests and Type Check are the evidence.Gates (static, run here)
lint-no-raw-table-writes, gate 32lint-skill-coverage --base origin/main, gate 36lint-sync-schema --base origin/main, gate 37lint-row-identity-coverage --base origin/main, gate 38lint-sync-write-invariants --base origin/main, and the changeset lint.Known follow-ups (from the changeset)
🤖 Generated with Claude Code