Snapshot-Store-Backend
ObjectStorageSnapshotStore ist die Snapshot-Store-
Implementierung, die
Object Storage als
Backing-Schicht verwendet.
import { ObjectStorageSnapshotStore, ObjectStorageSnapshotStoreOptions, S3ObjectStorageBackend, S3ObjectStorageOptions, PersistenceExtensionId,} from 'actor-ts';
const s3ObjectStorageOptions = S3ObjectStorageOptions.create() .withRegion(region) .withBucket(bucket);const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create() .withBackend(new S3ObjectStorageBackend(s3ObjectStorageOptions)) .withCompression({ algorithm: 'gzip' }) .withEncryption(encryption);const snapshotStore = new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions);
system.extension(PersistenceExtensionId).setJournal(someJournal);system.extension(PersistenceExtensionId).setSnapshotStore(snapshotStore);Snapshots, die von jedem PersistentActor geschrieben werden,
gehen in den S3-Bucket; komprimiert + verschlüsselt gemäß der
Config.
Wann verwenden
Abschnitt betitelt „Wann verwenden“Drei Muster:
- Cluster-weit geteilte Snapshots — Sharded Entities, die zwischen Nodes wandern, brauchen, dass jeder Node den Snapshot jeder Entity laden kann. Object Storage funktioniert; SQLite pro Node nicht.
- Verschlüsselte Snapshots — Server-Side- und/oder Client-Side-Verschlüsselung at rest für Compliance erforderlich.
- Günstiger Snapshot-Storage — Object Storage ist pro GB viel günstiger als SQL-Stores für selten-gelesene-gelegentlich-überschriebene Daten.
Für Single-Node-Deployments ist SqliteSnapshotStore schneller + einfacher.
Konfiguration
Abschnitt betitelt „Konfiguration“type ObjectStorageSnapshotStoreOptionsType = { backend: ObjectStorageBackend; prefix?: string; // Default '' keepN?: number; // Default 3 compression?: CompressionConfig | CompressionResolver; encryption?: EncryptionConfig | EncryptionResolver; maxDecompressedBytes?: number; // Default 512 MiB};| Feld | Zweck |
|---|---|
backend | Filesystem- oder S3-Backend. |
prefix | Object-Key-Prefix. Nützlich, um Buckets zu teilen. |
keepN | Pro persistenceId behaltene Snapshots; ältere werden beim Save entfernt. Default 3. |
compression | At-Rest-Kompression — siehe Kompression. |
encryption | At-Rest-Verschlüsselung — siehe Verschlüsselung. |
maxDecompressedBytes | Schutz vor Dekompressions-Bomben — Obergrenze für die Größe eines dekodierten Snapshots. Default 512 MiB. |
Key-Layout
Abschnitt betitelt „Key-Layout“<prefix>/<persistenceId>/seq-<seqNr>Beispiele:
snapshots/account-42/seq-100snapshots/account-42/seq-200snapshots/account-42/seq-300Das Framework listet Keys unter <prefix>/<persistenceId>/, um
den neuesten Snapshot zu finden.
Für sehr große persistenceId-Räume ist das Listen pro pid in S3
typischerweise schnell (Per-Prefix-Durchsatz). Vermeide es, alle
Entity-Typen unter denselben Prefix ohne Per-pid-Unterordner zu
packen.
Performance
Abschnitt betitelt „Performance“Snapshot-Writes gehen durch das PUT von Object Storage; Reads durch GET. Zahlen für S3 Same-Region:
- Save — 20-50 ms pro Snapshot.
- Load latest — 1 LIST + 1 GET = 30-60 ms.
Für Hot-Path-Snapshot-Laden (häufige Actor-Neustarts) wickle es mit CachedSnapshotStore ein:
const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create().withBackend(backend);const cachedSnapshotStoreOptions = CachedSnapshotStoreOptions.create().withCache(cache);const cached = new CachedSnapshotStore( new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions), cachedSnapshotStoreOptions,);Reduziert redundante S3-GETs auf Sub-Mikrosekunden-Cache-Hits.
Per-Actor-Overrides
Abschnitt betitelt „Per-Actor-Overrides“class Account extends PersistentActor<...> { protected compression() { return { algorithm: 'zstd' as const }; } protected encryption() { return { keyRing: accountKeyRing }; }}Per-Actor-Konfiguration gilt für Snapshots auf dieselbe Weise wie für Durable State. Siehe Per-Actor-Policies.
Snapshot-Lifecycle
Abschnitt betitelt „Snapshot-Lifecycle“PersistentActor.persist(event) gelingt ↓ snapshotPolicy() prüfen ↓ wenn true → Snapshot machenbackend.put('snapshots/<pid>/seq-N', serialisierter State)
PersistentActor.preStart ↓ 'snapshots/<pid>/' auflisten, um die neueste seq zu finden ↓ backend.get(latest) → dekodieren → onEvent ab seq+1Das Framework handhabt Snapshot-Saves + -Loads über dieses Layout; du setzt die Policy.
Cleanup alter Snapshots
Abschnitt betitelt „Cleanup alter Snapshots“const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create() .withBackend(backend) .withKeepN(5);new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions); // die 5 neuesten behalten; ältere löschenPruning ist standardmäßig aktiv: keepN ist per Default 3, sodass
jeder Save nur die neuesten Snapshots pro persistenceId behält und
die älteren löscht. Erhöhe oder senke die Grenze mit withKeepN;
setze keepN: 0, um Pruning zu deaktivieren und jeden Snapshot zu
behalten.
Das Framework löscht beim Lesen nicht automatisch — es gibt ein kleines Fenster, in dem alte Snapshots mit neuen koexistieren.
Mischen mit dem Journal
Abschnitt betitelt „Mischen mit dem Journal“{ const sqliteJournalOptions = SqliteJournalOptions.create().withPath('...'); const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create().withBackend(backend); journal: new SqliteJournal(sqliteJournalOptions), snapshotStore: new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions),}Das Journal und der Snapshot-Store sind unabhängig. Häufige Muster:
- SQLite-Journal + ObjectStorage-Snapshots — lokale Event-Rate, geteilte Snapshots für Sharded Entities.
- Cassandra-Journal + ObjectStorage-Snapshots — beide über den Cluster geteilt.
- ObjectStorage alles — wenn S3 dein einziger Storage ist. Langsamer pro Op, aber billig.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Object Storage im Überblick — das größere Bild.
- Snapshot Stores im Überblick — allgemeine Snapshot-Policy.
- CachedSnapshotStore — Read-Through-Cache.
- Per-Actor-Policies — Per-Actor-Kompression / -Verschlüsselung.
