Skip to content

Choosing a Backend

mempill ships two persistence backends. Both pass the shared behavioral conformance harness, so your claim logic works identically on either — only the deployment characteristics differ.

Criterion SQLite (topology-a) PostgreSQL (topology-b)
Setup Zero config — file per agent Requires running PG 16+ instance
Multi-agent One DB file per agent_id Shared DB; true per-agent concurrency
Write locking Single-connection serialized writes pg_advisory_xact_lock per agent_id
Connection pool Single connection r2d2 pool (max 20)
Production TLS N/A Planned future release (NoTls only today)
Vector search NoOp (structural seam only) NoOp (structural seam only)
Best for Single-agent, embedded, tests, MCP Multi-agent production service

SQLite is the default backend and requires no external process. The adapter opens one database file per agent_id and applies mandatory PRAGMAs at connection open:

PRAGMA journal_mode = WAL;
PRAGMA synchronous = FULL;
PRAGMA foreign_keys = ON;

synchronous=FULL is non-negotiable. WAL + NORMAL can lose committed writes on an unclean shutdown. All writes are serialized through a single connection per process — the engine’s per-agent lock map and a store-level write lock enforce this invariant.

use mempill_sqlite::{open_default, open_default_in_memory};
// File-backed (durable)
let engine = open_default("/path/to/agent.db")?;
// In-memory (ephemeral — useful for tests and MCP sessions)
let engine = open_default_in_memory()?;
  • Single-agent embedded deployment
  • CLI tools, MCP adapters, local development
  • Integration tests (in-memory mode, zero config)
  • Situations where an external database process is undesirable

PostgreSQL (topology-b) — shared database

Section titled “PostgreSQL (topology-b) — shared database”

The PostgreSQL adapter targets multi-agent deployments where multiple agents share a single database. It uses an r2d2 connection pool (max 20) and serializes same-agent writes via pg_advisory_xact_lock(hashtext(agent_id)::bigint). An optimistic concurrency control (OCC) constraint (UNIQUE(agent_id, stream_seq) on ledger_entries) provides belt-and-suspenders protection against concurrent write anomalies.

Schema is managed by a refinery migration embedded at compile time (V1 — runs once on first connect). Tested against PostgreSQL 16 and 18.4.

Rust:

use mempill_postgres::open_postgres;
use mempill_core::{EngineConfig, NoOpOracle, NoOpVector};
let engine = open_postgres(
"host=localhost port=5432 user=mempill dbname=mempill password=secret",
None::<std::sync::Arc<NoOpOracle>>,
None::<std::sync::Arc<NoOpVector>>,
EngineConfig::default(),
)?;
  • Multi-agent deployments (several agents writing concurrently to a shared database)
  • Production service with external persistence
  • Scenarios requiring PostgreSQL-level backup and replication

Both adapters are verified behaviorally identical by the shared run_persistence_conformance harness in mempill-core (compiled under the test-support feature flag). The conformance suite runs against both backends in CI. Any claim that produces CommittedCheap on SQLite produces CommittedCheap on PostgreSQL for the same input.

  • Start with SQLite (open_default or open_default_in_memory). It requires no external process and is the default for tests, MCP adapters, and single-agent tools.
  • Migrate to PostgreSQL when you need multiple agents sharing a single database or production-grade persistence with connection pooling.
  • Never mix the two in a single deployment.