Expand description
A minimal, in-memory state manager with durable WAL logging.
ministate provides a simple yet robust way to maintain mutable application state
that survives process restarts, using an append-only Write-Ahead Log (WAL).
It builds directly on ministore for reliable, human-readable journaling.
§Core Features
- In-memory state: Fast reads via
RwLock, fullCloneon demand. - Durable mutations: Every change is first written to disk (
fsync-ed) before being applied. - Crash recovery: On startup, the state is reconstructed by replaying the WAL from the beginning.
- Logical ordering: Each mutation is assigned a monotonically increasing sequence number.
§Optional Snapshot Support (snapshot feature)
When the snapshot Cargo feature is enabled, ministate integrates with [minisnap] to:
- Save full state snapshots to disk for faster recovery.
- Enable future WAL compaction (truncating the log prefix after a snapshot is taken).
⚠️ Snapshotting is explicit — you must call
create_snapshot()manually. WAL compaction is not yet implemented but will be added in a future release.
§Concurrency Model
- Reads: Concurrent via
snapshot()(usesRwLock::read). - Writes: Serialized via
apply()(usesRwLock::write). - No hidden threads: All I/O is explicit and
await-driven.
§Guarantees
- Durability: If
apply().awaitreturnsOk(_), the mutation is guaranteed to be on disk. - Atomicity: The in-memory state is updated only if the WAL write succeeds.
- Ordering: Mutations are applied in the exact order they appear in the WAL.
- Recoverability: Full state restoration is possible from the WAL alone (or WAL + snapshot).
§Use Cases
- Stateful services that must recover after a crash (e.g., component registries, deployment specs).
- Embedded systems with limited resources but strict durability requirements.
- Local coordination primitives (e.g., leader election state, queue metadata).
§Example
use ministate::{Mutator, StateManager};
use serde::{Deserialize, Serialize};
#[derive(Default, Clone, Serialize, Deserialize)]
struct Counter { value: u32 }
#[derive(Serialize, Deserialize)]
struct Inc { by: u32 }
impl Mutator<Counter> for Inc {
fn apply(&self, state: &mut Counter) {
state.value += self.by;
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let tmp = tempfile::tempdir()?;
let dir = tmp.path();
let mgr = StateManager::open(dir, "counter.wal.jsonl").await?;
mgr.apply(Inc { by: 10 }).await?;
assert_eq!(mgr.snapshot().await.value, 10);
Ok(())
}Structs§
- State
Manager - Manages in-memory state with durable WAL logging.
Enums§
Traits§
- Mutator
- A mutation that can be applied to a state
S.