mire
A small PostgreSQL event-sourcing library for Rust. Append-only
event streams, optimistic concurrency, snapshots, replica-safe
projections via lease + fence-token, and an escape hatch for the
rare read-before-write case. Backed by sqlx + tokio. Only
dependency: Postgres.
Install
[]
= "0.1"
Pre-1.0 — the API can shift between minor releases. Pin a patch
(= "0.1.0") if you need stability across the next change.
Quickstart
use ;
use ;
use PgPool;
async
That's the shape of every interaction with mire: load → record → save, and load again to replay state from the log.
Patterns
Each example below is a self-contained slice — one concept, one runnable program, a short "why this pattern exists" block at the top. Pick the one matching what you're trying to do.
| Pattern | When to use | Example |
|---|---|---|
| Aggregate lifecycle | The minimum shape. Defining events, an aggregate, load/record/save. Every other pattern builds on this. | bank_account |
| Commands & validation | Refusing invalid operations before recording events. The right place for "can't overdraft", "can't operate on a closed account", etc. | commands |
| Concurrency conflicts | Two writers race on the same aggregate. mire uses optimistic concurrency — no locks; the loser retries. | concurrency |
| Projections (read models) | Aggregates are great for writing; terrible for querying. Build a query-optimised table the runner keeps current. | projection |
| Snapshots | A single aggregate has accumulated >1k events and load is on a hot path. Cache the folded state so loads stay O(1). | snapshot |
| Read-before-write across aggregates | One business decision spans two aggregates (e.g. atomic money transfer). The escape hatch — used sparingly. | transaction_scope |
| Multi-replica deployment | Production runs multiple replicas. Exactly one drives each projection at a time, with automatic failover. | multi_replica |
| High-throughput writes | Bulk ingest. Calling save once per event is 10× slower than necessary; batch them. |
batched_write |
Running the examples
# … etc.
If you don't use mise:
What mire is not
- Not a queue. Events go into a per-stream log, not a Kafka-style topic. Use mire when your durable state IS your event log.
- Not multi-region. Single Postgres cluster, with the usual Postgres-replication options for read replicas / DR.
- Not a CRUD ORM. State lives in events; aggregates are
reconstructed by replay. If you want to
UPDATE … SET …, use sqlx directly.
License
Licensed under MIT