Snapshot store backend
이 콘텐츠는 아직 번역되지 않았습니다.
ObjectStorageSnapshotStore is the snapshot-store
implementation that uses
object storage
as its backing layer.
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 written by every PersistentActor go to the S3
bucket; compressed + encrypted per the config.
When to use it
Section titled “When to use it”Three patterns:
- Cluster-wide shared snapshots — sharded entities that move between nodes need any node to be able to load any entity’s snapshot. Object storage works; SQLite per-node doesn’t.
- Encrypted snapshots — server-side and/or client-side encryption at rest required for compliance.
- Cheap snapshot storage — object storage is far cheaper per GB than SQL stores for read-rarely-rewrite-occasionally data.
For single-node deployments, SqliteSnapshotStore is faster + simpler.
Configuration
Section titled “Configuration”type ObjectStorageSnapshotStoreOptionsType = { backend: ObjectStorageBackend; prefix?: string; // default '' keepN?: number; // default 3 compression?: CompressionConfig | CompressionResolver; encryption?: EncryptionConfig | EncryptionResolver; maxDecompressedBytes?: number; // default 512 MiB};| Field | Purpose |
|---|---|
backend | Filesystem or S3 backend. |
prefix | Object-key prefix. Useful for sharing buckets. |
keepN | Snapshots retained per persistenceId; older ones are pruned on save. Default 3. |
compression | At-rest compression — see Compression. |
encryption | At-rest encryption — see Encryption. |
maxDecompressedBytes | Decompression-bomb guard — cap on a decoded snapshot’s size. Default 512 MiB. |
Key layout
Section titled “Key layout”<prefix>/<persistenceId>/seq-<seqNr>Examples:
snapshots/account-42/seq-100snapshots/account-42/seq-200snapshots/account-42/seq-300The framework lists keys under <prefix>/<persistenceId>/ to
find the latest snapshot.
For very-large persistenceId spaces, listing per-pid is
typically fast in S3 (per-prefix throughput). Avoid putting
all entity types under the same prefix without per-pid
subdirectories.
Performance
Section titled “Performance”Snapshot writes go through object-storage’s PUT; reads through GET. Numbers for S3 same-region:
- Save — 20-50 ms per snapshot.
- Load latest — 1 LIST + 1 GET = 30-60 ms.
For hot-path snapshot loading (frequent actor restarts), wrap with CachedSnapshotStore:
const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create().withBackend(backend);const cachedSnapshotStoreOptions = CachedSnapshotStoreOptions.create().withCache(cache);const cached = new CachedSnapshotStore( new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions), cachedSnapshotStoreOptions,);Reduces redundant S3 GETs to sub-microsecond cache hits.
Per-actor overrides
Section titled “Per-actor overrides”class Account extends PersistentActor<...> { protected compression() { return { algorithm: 'zstd' as const }; } protected encryption() { return { keyRing: accountKeyRing }; }}Per-actor configuration applies to snapshots the same way as durable state. See Per-actor policies.
Snapshot lifecycle
Section titled “Snapshot lifecycle”PersistentActor.persist(event) succeeds ↓ check snapshotPolicy() ↓ if true → take snapshotbackend.put('snapshots/<pid>/seq-N', serialized state)
PersistentActor.preStart ↓ list 'snapshots/<pid>/' to find latest seq ↓ backend.get(latest) → decode → onEvent from seq+1 onwardsThe framework handles snapshot saves + loads via this layout; you set the policy.
Cleanup of old snapshots
Section titled “Cleanup of old snapshots”const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create() .withBackend(backend) .withKeepN(5);new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions); // keep 5 most recent; delete olderPruning is on by default: keepN defaults to 3, so each save
retains only the most-recent snapshots per persistenceId and
deletes the older ones. Raise or lower the bound with
withKeepN; set keepN: 0 to disable pruning and keep every
snapshot.
The framework doesn’t auto-delete during reads — there’s a small window where old snapshots co-exist with new ones.
Mixing with the journal
Section titled “Mixing with the journal”{ const sqliteJournalOptions = SqliteJournalOptions.create().withPath('...'); const objectStorageSnapshotStoreOptions = ObjectStorageSnapshotStoreOptions.create().withBackend(backend); journal: new SqliteJournal(sqliteJournalOptions), snapshotStore: new ObjectStorageSnapshotStore(objectStorageSnapshotStoreOptions),}The journal and snapshot store are independent. Common patterns:
- SQLite journal + ObjectStorage snapshots — local event-rate, shared snapshots for sharded entities.
- Cassandra journal + ObjectStorage snapshots — both shared across the cluster.
- ObjectStorage everything — when S3 is your only storage. Slower per-op but cheap.
Where to next
Section titled “Where to next”- Object storage overview — the bigger picture.
- Snapshot stores overview — general snapshot policy.
- CachedSnapshotStore — read-through cache.
- Per-actor policies — per-actor compression / encryption.
