Object Storage im Überblick
Das Object-Storage-Backend des Frameworks ist eine S3-kompatible Persistenz-Schicht. Zwei Implementierungen werden mitgeliefert:
| Backend | Verwendung |
|---|---|
FilesystemObjectStorageBackend | Lokale Dateien; Dev + Tests. |
S3ObjectStorageBackend | Alles S3-kompatible (AWS S3, MinIO, R2, B2). |
Verwendet von:
ObjectStorageDurableStateStore— Durable State im Cloud-Storage.ObjectStorageSnapshotStore— Snapshots im Cloud-Storage.
Gebaut auf einem kleinen Interface (PUT / GET / DELETE / LIST + CAS-Unterstützung); beide Backends sprechen dieselbe Oberfläche.
Wann verwenden
Abschnitt betitelt „Wann verwenden“| Du solltest Object Storage verwenden, wenn… |
|---|
| Du Cloud-native bist und S3 (oder ähnlich) deine Storage-Plattform ist. |
| Du geteilte Persistenz über Cluster-Nodes willst, ohne Cassandra zu betreiben. |
| Du Server-Side-Encryption über Cloud-KMS willst. |
| Du unendliches Scaling willst, ohne Storage-Kapazität zu verwalten. |
Für Single-Node-Deployments ist SQLite einfacher. Für High-Throughput-Multi-Node-Persistenz ist Cassandra schneller. Object Storage sitzt dazwischen — cloud-freundlich, anständige Performance, viele Features.
Ein minimales Beispiel
Abschnitt betitelt „Ein minimales Beispiel“import { DurableStateOptions, ObjectStorageDurableStateStore, ObjectStorageDurableStateStoreOptions, S3ObjectStorageBackend, S3ObjectStorageOptions,} from 'actor-ts';
const s3ObjectStorageOptions = S3ObjectStorageOptions.create() .withRegion('eu-west-1') .withBucket('my-app-state');const backend = new S3ObjectStorageBackend(s3ObjectStorageOptions);
const objectStorageDurableStateStoreOptions = ObjectStorageDurableStateStoreOptions.create().withBackend(backend);const stateStore = new ObjectStorageDurableStateStore( objectStorageDurableStateStoreOptions,);
const durableStateOptions = DurableStateOptions.create<State>() .withPersistenceId(`cart-${userId}`) .withStore(stateStore) .withEmptyState(() => ({ items: [] }));const cart = system.spawnAnonymous(() => new Cart(durableStateOptions));Der State von Cart lebt in S3 unter cart-<userId> als
Object-Key. Reads und Writes gehen durch die S3-API.
Was gespeichert wird
Abschnitt betitelt „Was gespeichert wird“Object-Keys folgen einem vorhersagbaren Layout:
state/ cart-user-42 # ein Object pro persistenceId cart-user-43
snapshots/ account-42/ seq-100 # Snapshots indexiert nach seqNr seq-200 seq-300Das Framework verwaltet dieses Layout; du baust keine Keys manuell.
Das Interface
Abschnitt betitelt „Das Interface“interface ObjectStorageBackend { put(key: string, body: Uint8Array, options?: PutOptions): Promise<{ etag: string }>; get(key: string): Promise<Option<ObjectFetched>>; delete(key: string): Promise<void>; list(options: { prefix: string; limit?: number }): Promise<ObjectInfo[]>;}Kleine Oberfläche — passt zu AWS S3, MinIO, Cloudflare R2, Backblaze B2, Wasabi, etc. Die meisten S3-kompatiblen APIs passen exakt.
CAS für optimistische Concurrency
Abschnitt betitelt „CAS für optimistische Concurrency“await backend.put('state/cart-42', body, { ifMatch: 'previous-etag',});ifMatch lässt Aufrufer Compare-and-Set-Writes machen — wenn
das aktuelle ETag abweicht, schlägt das Put mit
ObjectStorageConcurrencyError fehl. Verwendet von
ObjectStorageDurableStateStore, um nebenläufige Writer ohne
separate Koordination zu erkennen.
ifNoneMatch: '*' ist Create-only — gelingt nur, wenn der
Key noch nicht existiert.
Einige ältere S3-kompatible Stores beachten diese Header nicht richtig. Die Backend-Implementierungen des Frameworks werfen klar in diesem Fall, anstatt still zu ignorieren; prüfe die CAS-Unterstützung deines Providers, bevor du dich darauf verlässt.
Backends
Abschnitt betitelt „Backends“Filesystem
Abschnitt betitelt „Filesystem“import { FilesystemObjectStorageBackend, FilesystemObjectStorageOptions } from 'actor-ts';
const filesystemObjectStorageOptions = FilesystemObjectStorageOptions.create().withDir('/var/lib/actor-ts');const backend = new FilesystemObjectStorageBackend( filesystemObjectStorageOptions,);Speichert Objects als Dateien unter rootDir. Kein Netzwerk,
keine S3-Kosten. Richtig für:
- Tests — gleicher Code-Pfad wie Produktion, mit lokalen Dateien.
- Lokales Dev — kein MinIO-Container erforderlich.
- Kleine Single-Node-Deployments — wenn du speziell das Object-Storage-Interface ohne S3 willst.
import { S3ObjectStorageBackend, S3ObjectStorageOptions } from 'actor-ts';
const s3ObjectStorageOptions = S3ObjectStorageOptions.create() .withRegion('eu-west-1') .withBucket('my-app-state') .withEndpoint('https://s3.eu-west-1.amazonaws.com') // optionaler Override .withCredentials({ accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, });const backend = new S3ObjectStorageBackend(s3ObjectStorageOptions);Funktioniert mit jedem S3-kompatiblen Service. Für Nicht-AWS:
// MinIO:const s3ObjectStorageOptions = S3ObjectStorageOptions.create() .withRegion('us-east-1') .withBucket('my-bucket') .withEndpoint('http://minio:9000') .withForcePathStyle(true);new S3ObjectStorageBackend(s3ObjectStorageOptions);
// Cloudflare R2:const s3ObjectStorage2Options = S3ObjectStorageOptions.create() .withRegion('auto') .withBucket('my-bucket') .withEndpoint('https://<account-id>.r2.cloudflarestorage.com');new S3ObjectStorageBackend(s3ObjectStorage2Options);
// Backblaze B2:const s3ObjectStorage3Options = S3ObjectStorageOptions.create() .withRegion('us-west-002') .withBucket('my-bucket') .withEndpoint('https://s3.us-west-002.backblazeb2.com');new S3ObjectStorageBackend(s3ObjectStorage3Options);Optionale Features
Abschnitt betitelt „Optionale Features“| Feature | Seite |
|---|---|
| Kompression (gzip / zstd) | Kompression |
| Verschlüsselung at rest (AES-GCM) | Verschlüsselung |
| Master-Key-Rotation | Schlüsselrotation |
| Per-Actor-Kompressions- / Verschlüsselungs-Policies | Per-Actor-Policies |
| Snapshot-Store-Backend | Snapshot-Store-Backend |
Alle optional — starte ohne; schichte nach Bedarf darauf.
Performance
Abschnitt betitelt „Performance“Grobe Zahlen für S3:
- Put (kleines Objekt): 20-50 ms.
- Get: 10-30 ms.
- Delete: 30-50 ms.
Filesystem-Backend: Sub-Millisekunde.
Object Storage ist langsamer als SQLite / Cassandra für einzelne Operationen. Kompensiere mit:
- Snapshot-Policies, die die Recovery begrenzen.
- CachedSnapshotStore-Decorator für Read-Through-Caching.
- Batching, wo immer das Framework es erlaubt.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Kompression — gzip / zstd At-Rest-Kompression.
- Verschlüsselung — AES-GCM-Verschlüsselung at rest.
- Snapshot-Store-Backend — Snapshots in Object Storage.
- Durable State — der Haupt-Konsument.
- Persistenz im Überblick — das größere Bild.
