Skip to main content

Module transaction

Module transaction 

Source
Expand description

ConcurrentTx — per-Connection BEGIN CONCURRENT transaction state (Phase 11.4).

Per docs/concurrent-writes-plan.md:

BEGIN CONCURRENT doesn’t acquire any locks; writes go to the version chain tagged with the transaction id; reads use snapshot-isolation visibility.

§How this slice does it

Each Connection owns at most one ConcurrentTx at a time. When the user issues BEGIN CONCURRENT, the connection deep- clones the database’s tables map into ConcurrentTx::tables and stores a TxHandle (which advances the MvccClock to allocate a begin_ts). Subsequent INSERT / UPDATE / DELETE statements run against the cloned tables (the executor thinks it’s writing to the live database — Connection swaps the cloned tables in just for the duration of each statement). The live Database::tables stays unchanged until commit.

At COMMIT:

  1. Diff tx.tables_at_begin (the immutable BEGIN-time clone) vs tx.tables (post-write) to derive a write-set: every (RowID, payload) the transaction changed.
  2. For each row in the write-set, walk the super::MvStore chain. If any committed version’s begin > tx.begin_ts, ABORT with crate::error::SQLRiteError::Busy — some other transaction touched the row after our snapshot.
  3. On success, allocate a commit_ts, push each write into the MvStore as a committed version (caps the previous latest version’s end at commit_ts), apply the writes to db.tables, and run the legacy save_database so changes persist via the existing WAL.

ROLLBACK just drops the ConcurrentTx — the cloned tables are released, the TxHandle drops (unregistering the transaction from ActiveTxRegistry), and db.tables is unchanged because we never touched it.

§What this slice doesn’t do (yet)

  • Snapshot-isolated reads inside the transaction. Reads inside BEGIN CONCURRENT see the cloned-at-BEGIN state of the tables (because the executor is dispatched against tx.tables), but they don’t consult MvStore to filter by begin_ts. Concurrent writes from outside the tx land on db.tables, not on our snapshot — so we don’t see them inside the tx. That’s partial snapshot isolation: it isolates correctly under the current “lock the database per statement” mutex, but doesn’t survive once the engine genuinely supports overlapping in-flight transactions reading concurrently.
  • DDL inside BEGIN CONCURRENT. v0 rejects with a typed error before the swap, mirroring the plan’s stated non-goal.
  • AUTOINCREMENT. Same — rejected with a typed error.
  • Persistence of the in-flight write-set across crashes. The write-set lives entirely in memory until commit. A crash mid-transaction loses everything — that’s correct (the transaction never committed), and the legacy WAL still owns durability of Database::tables for committed data. Phase 11.5 adds the MVCC log-record frame format that lets writes start landing in the WAL pre-commit.

Structs§

ConcurrentTx
Per-Connection snapshot of BEGIN CONCURRENT state.