# How ai-memory Handles Concurrent Writes and Supersessions: Architecture Deep Dive

> Discover how ai-memory ensures race-free concurrent writes and handles supersessions using a single writer thread, version history, and SHA-256 hash checks. Learn the architecture.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: architecture-deep-dive
- Published: 2026-09-11

---

**ai-memory** guarantees race-free concurrent writes by serializing all mutating SQL operations through a single writer thread that maintains version history via a supersession chain, using SHA-256 hash checks to avoid duplicate versions.

The `akitaonrails/ai-memory` project implements a robust SQLite-backed storage layer for AI context management. To solve the classic "database is locked" problem while maintaining full edit history, the codebase employs a single-writer actor pattern combined with an immutable supersession chain. This architecture ensures that **concurrent writes and supersessions** are handled safely without explicit locks or complex conflict resolution.

## Single-Threaded Writer Architecture

In [`crates/ai-memory-store/src/writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/writer.rs), the `WriterHandle::spawn` method creates the sole `rusqlite::Connection` and confines it to a dedicated writer thread. All mutating operations—including page upserts, handoff inserts, and session moves—are funneled through a Tokio `mpsc` channel (`WriteCmd`) with a buffer of 1024 slots by default.

Each async caller dispatches a command paired with a `oneshot::Sender` for the reply. Because the writer processes commands sequentially, there is **exactly one writer at any time**, eliminating race conditions and the "database is locked" error mode entirely.

## The Supersession Chain Mechanism

When content changes, `ai-memory` does not overwrite existing rows. Instead, `ops::upsert_page` in [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs) implements a **supersession chain**:

- **New versions** are inserted with `superseded_at IS NULL`
- **Previous heads** are updated with a timestamp, linking the rows chronologically

### Hash-Based Short-Circuiting

Before creating a new version, `upsert_page` computes a SHA-256 hash of the page body. If the hash matches the current latest version, the function returns the existing `PageId` immediately, avoiding unnecessary supersessions and database writes.

### Atomic Transaction Boundaries

The entire upsert logic—hash comparison, conditional insert, and update of the previous row's `superseded_at`—executes inside a single SQLite transaction within the writer thread. This atomicity prevents concurrent upserts from interleaving and creating duplicate or orphaned versions.

## Scope-Aware Operations and Deletion Safety

The supersession chain persists across workspace and project boundaries. When executing `MoveSession`, the writer preserves the full version history even as pages migrate between scopes.

For deletions, `delete_page_if_latest` accepts an `expected_latest_id` parameter. The operation succeeds only if the supplied ID matches the current head of the supersession chain, preventing accidental removal of historical versions when concurrent edits occur.

## Practical Implementation Examples

This pattern ensures that even when many asynchronous tasks issue writes simultaneously, the writer's channel buffers them and processes them one-by-one, preserving data integrity.

```rust
// Upserting a page creates a new version only when content changes
let page = NewPage {
    workspace_id,
    project_id,
    path: page_path,
    title: "My Page".into(),
    body: "Updated content".into(),
};
let page_id = writer_handle.upsert_page(page).await?;

```

```rust
// Deleting only works on the latest version, protecting the chain
let deleted = writer_handle
    .delete_page_if_latest(workspace_id, project_id, page_path, latest_id)
    .await?;
assert!(deleted, "Only the head of the supersession chain may be removed");

```

```rust
// Moving sessions preserves supersession history across projects
writer_handle
    .move_session(
        session_id,
        target_workspace,
        target_project,
        PagesMode::Include,
        None,
        false,
    )
    .await?;

```

## Summary

- **Single-writer serialization**: All mutations pass through `WriterHandle::spawn` and a Tokio `mpsc` channel, ensuring linearizable operations without database locks.
- **Immutable versioning**: The supersession chain in `ops::upsert_page` links versions via `superseded_at` timestamps, creating a complete audit trail.
- **Content-aware deduplication**: SHA-256 hashing prevents redundant supersessions when page content remains unchanged.
- **Atomic consistency**: SQLite transactions within the writer thread guarantee that hash checks, inserts, and updates succeed or fail together.
- **Safe deletion guards**: `delete_page_if_latest` requires the caller to prove they hold the current head of the chain, preventing history corruption.

## Frequently Asked Questions

### How does ai-memory prevent "database is locked" errors during high concurrency?

By confining the `rusqlite::Connection` to a single writer thread and routing all writes through an `mpsc` channel, `ai-memory` ensures only one SQL statement executes at a time. This eliminates the busy-wait conditions that cause SQLite locking errors in multi-threaded scenarios.

### What happens when two clients try to update the same page simultaneously?

Both requests queue in the writer's channel and process sequentially. The first completes a full upsert (hash check, insert, supersession update). The second performs its own hash check against the new head; if the content differs, it creates another version in the chain. The linear execution guarantees a valid chronological history without merge conflicts.

### Can old versions of a page be deleted without breaking the supersession chain?

No. The `delete_page_if_latest` function requires the `expected_latest_id` parameter to match the current head. Attempting to delete an older version in the middle of the chain fails, ensuring the linked history remains intact and traversable via [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs).

### Does moving a page between projects preserve its edit history?

Yes. The `MoveSession` command preserves the `superseded_at` linkage. The page's entire version history remains accessible after migration because the supersession chain stores workspace and project associations per version rather than globally.