DoltLite does not implement SQLite's super-journal protocol for two-phase commit across multiple attached databases. A `COMMIT` that touches two or more ATTACHed doltlite databases is not atomic: a crash between phase-one and phase-two can leave the databases in inconsistent states (one committed, the other not).
What works
- Single-database commits are atomic and durable.
- Multi-table commits within a single doltlite database are atomic (they all go through one `chunkStoreCommit`).
- ATTACHing a stock-SQLite database to a doltlite connection commits the stock side via SQLite's normal pager; the doltlite side commits via the chunk store. Each is atomic on its own.
What doesn't work
- A transaction that writes to two or more ATTACHed doltlite databases:
ATTACH 'other.db' AS other;
BEGIN;
INSERT INTO main.t VALUES(...);
INSERT INTO other.t VALUES(...);
COMMIT; -- not atomic across main + other
Why
SQLite's atomic-multi-DB protocol uses a super-journal file. `vdbeCommit` in `src/vdbeaux.c` only takes the super-journal path when at least one attached DB reports a journal mode other than WAL or memory, AND when `xCommitPhaseOne` writes the changes to a recoverable journal before `xCommitPhaseTwo` finalizes them.
DoltLite's pager shim reports WAL unconditionally (`shimPagerGetJournalMode` → `PAGER_JOURNALMODE_WAL`), and `prollyBtreeCommitPhaseOne` is a no-op:
- `src/pager_shim.c` (`shimPagerGetJournalMode`)
- `src/prolly_btree.c:4509` (`prollyBtreeCommitPhaseOne`)
So the super-journal code path in `vdbeaux.c` is never selected for multi-DB transactions involving doltlite, and the existing phase-two does the full work of writing chunks + refs + fsync, with no rollback escape hatch once the first DB has committed.
Workaround
Do multi-database writes one DB at a time, or accept the cross-DB inconsistency window. `clone`-style use cases that need to ATTACH a remote doltlite db and stream-write to it are unaffected (single attached writer) — see #939.
Out of scope for now
We're explicitly deprioritizing this. Building a real two-phase commit on top of the chunk store + refs design is a non-trivial lift, and there's no live use case pushing for it. Tracking here so we don't forget; we'll come back to it when a user need surfaces or when doltlite hardens up enough that this becomes the next-most-likely correctness embarrassment.
Related
DoltLite does not implement SQLite's super-journal protocol for two-phase commit across multiple attached databases. A `COMMIT` that touches two or more ATTACHed doltlite databases is not atomic: a crash between phase-one and phase-two can leave the databases in inconsistent states (one committed, the other not).
What works
What doesn't work
Why
SQLite's atomic-multi-DB protocol uses a super-journal file. `vdbeCommit` in `src/vdbeaux.c` only takes the super-journal path when at least one attached DB reports a journal mode other than WAL or memory, AND when `xCommitPhaseOne` writes the changes to a recoverable journal before `xCommitPhaseTwo` finalizes them.
DoltLite's pager shim reports WAL unconditionally (`shimPagerGetJournalMode` → `PAGER_JOURNALMODE_WAL`), and `prollyBtreeCommitPhaseOne` is a no-op:
So the super-journal code path in `vdbeaux.c` is never selected for multi-DB transactions involving doltlite, and the existing phase-two does the full work of writing chunks + refs + fsync, with no rollback escape hatch once the first DB has committed.
Workaround
Do multi-database writes one DB at a time, or accept the cross-DB inconsistency window. `clone`-style use cases that need to ATTACH a remote doltlite db and stream-write to it are unaffected (single attached writer) — see #939.
Out of scope for now
We're explicitly deprioritizing this. Building a real two-phase commit on top of the chunk store + refs design is a non-trivial lift, and there's no live use case pushing for it. Tracking here so we don't forget; we'll come back to it when a user need surfaces or when doltlite hardens up enough that this becomes the next-most-likely correctness embarrassment.
Related