SQLite-Snapshot-Store
SqliteSnapshotStore persistiert Snapshots in eine SQLite-Datei —
durable, null Dependency auf Bun, Peer-Dep auf Node. Paare es mit
SqliteJournal für das
Standard-Single-Node-Produktions-Setup.
import { SqliteJournal, SqliteJournalOptions, SqliteSnapshotStore, SqliteSnapshotStoreOptions, ActorSystem, ActorSystemOptions,} from 'actor-ts';
const sqliteJournalOptions = SqliteJournalOptions.create() .withPath('/var/lib/my-app/events.db') .withWal(true);const sqliteSnapshotStoreOptions = SqliteSnapshotStoreOptions.create().withPath('/var/lib/my-app/snapshots.db');const actorSystemOptions = ActorSystemOptions.create().withPersistence({ journal: new SqliteJournal(sqliteJournalOptions), snapshotStore: new SqliteSnapshotStore(sqliteSnapshotStoreOptions),});const system = ActorSystem.create('my-app', actorSystemOptions);Konfiguration
Abschnitt betitelt „Konfiguration“type SqliteSnapshotStoreOptions = { path?: string; // Dateipfad oder ':memory:' snapshotsTable?: string; // Default 'snapshots' keepN?: number; // max. Snapshots pro persistenceId, beim Speichern beschnitten (Default 3) busyTimeoutMs?: number; // Lock-Wartebudget, Default 1000; 0 = sofort scheitern driver?: SqliteDriver;};path, busyTimeoutMs und driver verhalten sich genau wie
beim SQLite-Journal —
einschließlich des Grundes, warum busyTimeoutMs überhaupt
einen vom Framework gesetzten Default hat: die Treiber liefern
unterschiedliche mit (0 ms bei bun:sqlite und node:sqlite,
5000 ms bei better-sqlite3), und das Warten ist synchron,
blockiert also die Event-Loop. Es gibt hier keine
wal-Option; stattdessen bietet der Snapshot-Store keepN
— die maximale Anzahl an Snapshots, die pro persistenceId
behalten werden, wobei ältere bei jedem Speichern beschnitten
werden (Default 3).
const sqliteSnapshotStoreOptions = SqliteSnapshotStoreOptions.create() .withPath('/var/lib/my-app/snapshots.db') .withBusyTimeoutMs(1000);new SqliteSnapshotStore(sqliteSnapshotStoreOptions)CREATE TABLE snapshots ( persistence_id TEXT NOT NULL, sequence_nr INTEGER NOT NULL, payload TEXT NOT NULL, timestamp INTEGER NOT NULL, PRIMARY KEY (persistence_id, sequence_nr));Gleiches Pattern wie das Journal: nach
(persistence_id, sequence_nr) geschlüsselt. Das
Framework liest die höchste sequence_nr für eine
gegebene persistence_id bei loadLatest.
Paarung mit dem Journal
Abschnitt betitelt „Paarung mit dem Journal“// Produktion:{ const sqliteJournalOptions = SqliteJournalOptions.create().withPath('/var/lib/events.db'); const sqliteSnapshotStoreOptions = SqliteSnapshotStoreOptions.create().withPath('/var/lib/snapshots.db'); journal: new SqliteJournal(sqliteJournalOptions), snapshotStore: new SqliteSnapshotStore(sqliteSnapshotStoreOptions),}Das Journal und der Snapshot-Store sind unabhängige Dateien. Du kannst sie auch in dieselbe Datei mit unterschiedlichen Tabellennamen packen — aber zwei Dateien halten Operationen einfacher (unabhängiges Backup, unabhängiges Vacuum).
Für Multi-Node-Deployments, in denen das Journal Cassandra ist, kannst du trotzdem SQLite für Snapshots verwenden, wenn jede Entity immer auf demselben Node wiederhergestellt wird. Aber für Sharded Entities (die zwischen Nodes wandern) muss der Snapshot-Store auch geteilt sein — siehe Object-Storage-Snapshot-Store.
Performance
Abschnitt betitelt „Performance“- Snapshot-Write — single INSERT, ~100 μs. Dominiert von der Serialisierung, nicht von SQLite-I/O.
- Snapshot-Read — single SELECT, Sub-Millisekunde.
Für sehr große Snapshots (Multi-MB-State) ist die Serialisierung der Bottleneck. Überlege Kompression (Object-Storage-Kompression) oder eine andere Snapshot-Strategie.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Snapshots — die Policy + Mechanik.
- SQLite-Journal — das Journal, mit dem du das paarst.
- In-Memory-Snapshot-Store — für Tests.
- Cached-Snapshot-Store — Read-Through-Cache.
Die SqliteSnapshotStore-API-
Referenz deckt die vollständigen Optionen ab.
