# Turso Transaction Isolation Levels and Concurrency Rules: A Deep Dive into MVCC

> Explore Turso transaction isolation levels and MVCC. Discover how Turso prevents dirty reads and detects write conflicts with concurrent readers and writers.

- Repository: [Turso Database/turso](https://github.com/tursodatabase/turso)
- Tags: deep-dive
- Published: 2026-06-22

---

**Turso implements Multiversion Concurrency Control (MVCC) with snapshot isolation as the default level, preventing dirty reads and ensuring write-write conflict detection while allowing concurrent readers and writers to proceed without blocking.**

Turso, the open-source database developed by tursodatabase/turso, handles concurrent access through a Rust-based MVCC engine that assigns every transaction a consistent snapshot of the database. This article examines how Turso transaction isolation levels and concurrency rules function at the source code level, including the specific mechanisms that prevent common anomalies and the file paths where these behaviors are defined.

## How Turso Implements Snapshot Isolation

Turso defaults to **snapshot isolation**, a consistency model that guarantees each transaction sees a stable view of the database as it existed at the moment the transaction began. The implementation relies on timestamp-ordered version chains and commit-time validation.

### Capturing the Snapshot

When a client issues `BEGIN CONCURRENT`, the SQL parser in [`core/parser/src/parser.rs`](https://github.com/tursodatabase/turso/blob/main/core/parser/src/parser.rs) recognizes this directive and initializes a transaction with a unique begin timestamp. This timestamp captures a read-only snapshot of all currently committed rows. The MVCC engine stores these versions in per-row chains, ensuring that the snapshot never drifts during the transaction lifetime. Unlike locking-based systems, this approach allows readers to proceed without acquiring locks, as they simply read historical versions that are visible to their transaction's timestamp.

### Enforcing Read Consistency

The [`core/mvcc/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/mod.rs) module explicitly defines the visibility rules that prevent **dirty reads** (uncommitted data) and **fuzzy reads** (later-committed changes). When a transaction reads a row, the engine traverses the version chain and returns only versions where `begin_ts <= transaction.begin_ts` and `end_ts` is either null (current version) or greater than the transaction's begin timestamp. This guarantees that readers never see data written by concurrent transactions that have not yet committed, and they never see changes committed after their own transaction began.

### Commit-Time Conflict Detection

Write operations are validated at commit time by the `check_version_conflicts` function in [`core/mvcc/database/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/database/mod.rs). Before a transaction can commit, the engine verifies that no other transaction has committed a later version of any row the current transaction modified. Specifically, the function checks for versions where `end_ts > tx.begin_ts` or in-flight versions owned by other active transactions. If a conflict exists, the transaction aborts with `LimboError::WriteWriteConflict`. The commit timestamp (`end_ts`) is strictly greater than the begin timestamp, ensuring monotonic ordering and eliminating edge cases where timestamps could collide.

## Concurrency Rules and Transaction Types

Turso distinguishes between concurrent read-write transactions and exclusive schema-modifying transactions, each governed by specific rules that prevent corruption while maximizing parallelism.

### Concurrent vs. Exclusive Transactions

Multiple `BEGIN CONCURRENT` transactions can execute simultaneously without blocking each other. Readers never block writers, and writers only block other writers when committing conflicting changes to the same row. However, **DDL statements** (such as `CREATE TABLE`, `DROP INDEX`, or schema changes) require an exclusive transaction initiated with `BEGIN` (without the `CONCURRENT` keyword). The executor in [`core/vdbe/execute.rs`](https://github.com/tursodatabase/turso/blob/main/core/vdbe/execute.rs) enforces this restriction, returning an error if DDL is attempted inside a concurrent transaction because schema modifications require an exclusive lock on the database catalog.

### Durability and Phantom Reads

`BEGIN CONCURRENT` transactions are **autocommit-durable**: once the commit succeeds, a WAL entry guarantees durability. However, the current implementation does not fully prevent **phantom reads**—anomalies where a second query in the same transaction sees new rows inserted by other transactions. The [`core/mvcc/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/mod.rs) file contains a TODO marking this as a known limitation, meaning range queries may observe phantom rows even under snapshot isolation.

## Practical Examples of Turso Isolation

The following examples demonstrate Turso's isolation semantics using both the SQL CLI and the Rust API.

### SQL CLI: Snapshot Isolation with Concurrent Transactions

```sql
-- Session A
BEGIN CONCURRENT;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- (transaction not yet committed)

-- Session B
BEGIN CONCURRENT;
SELECT balance FROM accounts WHERE id = 1;
-- Returns the balance *before* Session A's update
COMMIT;

-- Back to Session A
COMMIT;

```

Session B reads the snapshot taken at its `BEGIN CONCURRENT` statement, which predates Session A's uncommitted update. When Session A commits, it succeeds because Session B did not write a conflicting version.

### Rust API: Detecting Write-Write Conflicts

```rust
use turso::Connection;
use std::sync::Arc;

let db = Arc::new(turso::Database::open("my.db")?);
let conn1 = db.connect()?;
let conn2 = db.connect()?;

// Transaction 1 commits first
conn1.execute("BEGIN CONCURRENT")?;
conn1.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')")?;
conn1.execute("COMMIT")?;

// Transaction 2 attempts conflicting write
conn2.execute("BEGIN CONCURRENT")?;
let result = conn2.execute("INSERT INTO users (id, name) VALUES (1, 'Bob')");

match result {
    Err(e) => println!("Write-write conflict detected: {}", e), // Expected
    Ok(_) => println!("Unexpected success"),
}
conn2.execute("ROLLBACK")?;

```

The second transaction receives `LimboError::WriteWriteConflict` because `check_version_conflicts` in [`core/mvcc/database/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/database/mod.rs) discovers that a later-committed version of the primary key row already exists.

### Preventing Dirty Reads

```rust
// Transaction T1 writes but does not commit
conn1.execute("BEGIN CONCURRENT")?;
conn1.execute("UPDATE accounts SET balance = 0 WHERE id = 42")?;

// Transaction T2 reads the same row
conn2.execute("BEGIN CONCURRENT")?;
let balance: i64 = conn2
    .query_row("SELECT balance FROM accounts WHERE id = 42", [], |row| row.get(0))?;
    
println!("Balance seen by T2: {}", balance); // Prints original value, not 0

```

Transaction T2 reads the pre-transaction value because T1's changes are invisible to any snapshot taken before T1 commits. This behavior derives from the visibility check in `check_version_conflicts` that filters out uncommitted versions.

## Summary

- **Turso uses snapshot isolation** by default for all `BEGIN CONCURRENT` transactions, implemented via MVCC timestamp chains in [`core/mvcc/database/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/database/mod.rs).
- **Dirty reads and fuzzy reads are prevented** by filtering row versions based on the transaction's begin timestamp, as defined in [`core/mvcc/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/mod.rs).
- **Write-write conflicts are detected at commit time** by the `check_version_conflicts` function, which aborts conflicting transactions with `LimboError::WriteWriteConflict`.
- **DDL requires exclusive transactions** (`BEGIN` without `CONCURRENT`), enforced in [`core/vdbe/execute.rs`](https://github.com/tursodatabase/turso/blob/main/core/vdbe/execute.rs), while DML supports high concurrency with readers never blocking writers.
- **Phantom reads are not yet prevented**, marked as a TODO in the MVCC module.

## Frequently Asked Questions

### What isolation level does Turso use by default?

Turso defaults to **snapshot isolation** for all transactions initiated with `BEGIN CONCURRENT`. This level provides each transaction with a consistent snapshot of the database from its start time, preventing dirty reads, non-repeatable reads, and lost updates while allowing concurrent execution.

### How does Turso detect write-write conflicts?

Turso detects conflicts at commit time using the `check_version_conflicts` function in [`core/mvcc/database/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/database/mod.rs). The function scans the version chain for each modified row and aborts the transaction with `LimboError::WriteWriteConflict` if another transaction has committed a later version or holds an in-flight lock on the row.

### Can I run DDL statements inside a BEGIN CONCURRENT transaction?

No. Data Definition Language (DDL) statements such as `CREATE TABLE` or `DROP INDEX` require an exclusive transaction initiated with plain `BEGIN`. The executor in [`core/vdbe/execute.rs`](https://github.com/tursodatabase/turso/blob/main/core/vdbe/execute.rs) explicitly rejects DDL operations within concurrent transactions because schema changes require exclusive catalog locks.

### Does Turso prevent phantom reads?

Currently, Turso does **not** fully prevent phantom reads. While snapshot isolation prevents dirty reads and fuzzy reads, the MVCC implementation in [`core/mvcc/mod.rs`](https://github.com/tursodatabase/turso/blob/main/core/mvcc/mod.rs) contains a TODO indicating that phantom reads—where new rows appear in subsequent range queries within the same transaction—remain a known limitation.