# How Turso's Write-Ahead Log (WAL) Works: Architecture and Implementation

> Explore Turso's Write-Ahead Log WAL architecture. Discover how atomic locks vectored I/O and coordinated checkpointing ensure crash recovery and concurrent reads.

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

---

**Turso's Write-Ahead Log (WAL) is implemented in [`core/storage/wal.rs`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs) as a SQLite-compatible durability layer that uses atomic locks, vectored I/O, and coordinated checkpointing to ensure crash recovery while allowing concurrent reads.**

Turso extends SQLite's WAL mode with a custom Rust implementation that lives in the `tursodatabase/turso` repository. The WAL persists in a separate "`-wal`" file alongside the database, managed by the **`WalFile`** struct which implements the **`Wal`** trait. This architecture provides atomic durability, crash recovery, and high-performance concurrent access through a sophisticated coordination layer.

## Core Data Structures and Coordination

The WAL implementation centers on several key structures that manage state, coordination, and I/O.

**`WalFileShared`** ([`core/storage/wal.rs:2712`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L2712)) holds the in-process state including the maximum frame number, backfill counters, checksums, and lock metadata. This structure is shared across all connections within a single process.

**`WalCoordination`** ([`core/storage/wal.rs:664`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L664)) is a trait that abstracts the coordination backend, providing atomic operations for snapshot publishing, lock acquisition, and frame caching. The default implementation, **`InProcessWalCoordination`** ([`core/storage/wal.rs:889`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L889)), uses the **`TursoRwLock`** primitive—a 64-bit atomic that packs a 32-bit value, reader count, and writer bit into one cache line for fast synchronization ([`core/storage/wal.rs:742-774`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L742-L774)).

The public API is defined by the **`Wal`** trait ([`core/storage/wal.rs:997`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L997)), which exposes methods like `begin_write_tx`, `commit_prepared_frames`, `checkpoint`, and `truncate_wal`.

## Starting a Write Transaction

Write transactions begin with **`begin_write_tx`**, which accepts **`WalAutoActions`** bitflags to enable automatic checkpointing or log restarts.

```rust
wal.begin_write_tx(WalAutoActions::all_enabled())?;

```

The method first attempts to acquire the writer lock via the coordination backend. If the log has been fully back-filled (all frames checkpointed to the database file), the WAL header is **restarted** before writing. This restart is performed by **`WalCoordination::try_restart_log_for_write`**, which upgrades the read-mark-0 lock, rewrites the header, and clears the `initialized` flag ([`core/storage/wal.rs:1442-1455`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L1442-L1455)).

## Preparing and Appending Frames

During an active transaction, the engine serializes modified pages into WAL frames through a multi-phase process:

1. **`prepare_frames`** serializes each page into a frame, computes the rolling checksum using **`checksum_wal`**, and records metadata (page reference, frame ID, cumulative checksum) in a **`PreparedFrames`** struct ([`core/storage/wal.rs:334-354`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L334-L354)).

2. **`append_frames_vectored`** performs the actual I/O using vectored `pwritev` system calls to write all buffers efficiently, followed by an optional `fsync` via **`prepare_wal_finish`**.

3. **`commit_prepared_frames`** updates the in-memory metadata (max frame, checksum, transaction count) and publishes the new commit via **`WalCoordination::publish_commit`** ([`core/storage/wal.rs:892-904`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L892-L904)).

4. **`finalize_committed_pages`** clears dirty-page flags on the database cache and optionally syncs the DB file.

## Checkpointing and Back-filling

Checkpointing copies committed WAL frames into the main database file and advances the backfill counter. The **`checkpoint`** method ([`core/storage/wal.rs:1103-1130`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L1103-L1130)) accepts a **`CheckpointMode`**:

- **`CheckpointMode::Full`** copies frames to the DB file but retains the WAL.
- **`CheckpointMode::Truncate`** copies frames and then truncates the WAL to zero length.

The checkpoint process acquires the checkpoint lock (`acquire_checkpoint_guard`) and read-mark-0 lock for exclusive access. It determines the safe frame limit via **`determine_max_safe_checkpoint_frame`** (based on active read-marks), reads frames in batches using **`read_frames_batch`**, writes them to the database file, and advances the shared **`nbackfills`** counter via **`publish_backfill`**.

When truncating, **`truncate_wal`** zeroes the file and reinitializes the header via **`prepare_wal_header`**, which generates a new random salt and checksum ([`core/storage/wal.rs:141-148`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L141-L148)).

## Concurrency Control and Locking

Turso's WAL supports concurrent readers through a multi-version concurrency control scheme:

- **Read transactions** acquire a *read-mark* (one of five slots) that records the frame number visible to that reader. The lock-free value is stored inside **`TursoRwLock`**. The coordination layer provides **`try_begin_read_tx`** to select the best available read-mark.

- **Write transactions** acquire the exclusive writer lock via **`try_begin_write_tx`**. The writer holds read-mark-0 to ensure exclusive access during header restarts or checkpoints.

- **Checkpoint operations** obtain an additional checkpoint lock via **`try_checkpoint_lock`** to prevent concurrent checkpoints.

All lock acquisition logic resides in **`InProcessWalCoordination`** (e.g., **`try_write_lock`**, **`try_read_mark_shared`**, **`try_checkpoint_lock`**).

## Practical Code Examples

### Example 1: Simple Write Transaction

```rust
use turso::storage::wal::{Wal, WalAutoActions, CheckpointMode, FileSyncType};

fn write_page(wal: &impl Wal, page_no: u64, page_data: &[u8], db_size: u64) -> anyhow::Result<()> {
    // 1) Start transaction with auto-actions enabled
    wal.begin_write_tx(WalAutoActions::all_enabled())?;
    
    // 2) Prepare frames with checksum calculation
    let page_size = PageSize::from_bytes(page_data.len() as u32);
    let prepared = wal.prepare_frames(
        &[PageRef::new(page_no)],
        page_size,
        Some(db_size as u32),
        None,
    )?;
    
    // 3) Append frames via vectored I/O
    wal.append_frames_vectored(vec![PageRef::new(page_no)], page_size)?;
    
    // 4) Commit to make visible to readers
    wal.commit_prepared_frames(&[prepared]);
    
    // 5) Sync WAL to disk
    wal.sync(FileSyncType::Normal)?;
    
    // 6) End transaction
    wal.end_write_tx();
    
    // 7) Auto-checkpoint if needed
    if wal.should_checkpoint() {
        wal.checkpoint(&pager, CheckpointMode::Full)?;
    }
    
    Ok(())
}

```

### Example 2: Manual Checkpoint with Truncation

```rust
fn truncate_wal(wal: &impl Wal, pager: &Pager) -> anyhow::Result<()> {
    // Run TRUNCATE checkpoint - copies all frames then zeros the WAL
    let result = wal.checkpoint(
        pager, 
        CheckpointMode::Truncate { upper_bound_inclusive: None }
    )?;
    
    // Ensure truncate I/O completes
    wal.truncate_wal(&mut result.clone(), FileSyncType::Normal)?;
    
    Ok(())
}

```

## Summary

- **Turso's WAL** is implemented in [`core/storage/wal.rs`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs) as the **`WalFile`** struct implementing the **`Wal`** trait, providing SQLite-compatible durability.
- **Write transactions** use `begin_write_tx`, `prepare_frames`, `append_frames_vectored`, and `commit_prepared_frames` to ensure atomic, durable writes with rolling checksums.
- **Checkpointing** copies frames to the database file via `checkpoint` with modes `Full` or `Truncate`, advancing the `nbackfills` counter and potentially zeroing the WAL file.
- **Concurrency control** uses **`TursoRwLock`** and read-marks to allow concurrent readers while maintaining exclusive writer access and checkpoint coordination.
- **Automatic actions** (`WalAutoActions`) can trigger checkpoints or log restarts automatically after commits or when the log is fully back-filled.

## Frequently Asked Questions

### What is the difference between WalFileShared and WalCoordination?

**`WalFileShared`** ([`core/storage/wal.rs:2712`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L2712)) is a concrete struct holding the actual in-process state like frame counters and checksums, while **`WalCoordination`** ([`core/storage/wal.rs:664`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L664)) is a trait abstracting how this state is synchronized across connections. The default **`InProcessWalCoordination`** implements this trait for single-process scenarios using atomic operations on `WalFileShared`, but the trait allows for alternative backends that could support cross-process or distributed coordination.

### How does Turso's WAL handle crash recovery?

Crash recovery relies on the WAL file containing a complete history of committed transactions since the last checkpoint. When reopening a database, Turso reads the WAL header to determine the maximum valid frame and uses the checksums stored in each frame header to verify integrity. The **`begin_write_tx`** method checks if the log needs restarting (when `wal_max_frame == wal_total_backfilled`), and the checkpoint mechanism ensures the database file is only updated after WAL frames are fully synced to disk.

### When does the WAL file get truncated?

The WAL file is truncated to zero length when a **`CheckpointMode::Truncate`** checkpoint completes successfully ([`core/storage/wal.rs:141-148`](https://github.com/tursodatabase/turso/blob/main/core/storage/wal.rs#L141-L148)). This occurs after all frames have been back-filled to the database file and the `nbackfills` counter equals the maximum frame number. The truncation is guarded by the checkpoint lock and read-mark-0 to ensure no readers are accessing old frames.

### How does Turso handle concurrent reads during writes?

Turso uses **read-marks** (up to five slots) stored in **`TursoRwLock`** to allow multiple concurrent readers. When a write transaction commits via **`commit_prepared_frames`**, it publishes the new max frame number without blocking existing readers, who continue to see the snapshot captured when they acquired their read-mark. Readers are only blocked if they attempt to read a page that the writer is currently modifying, and the writer holds read-mark-0 during checkpoints to prevent new readers from starting while the database file is being updated.