Per-Actor-Policies
Das Object-Storage-Backend kann Store-Level-Defaults für Kompression + Verschlüsselung haben. Aber manchmal brauchen bestimmte Actors andere Einstellungen:
- Ein sensibler Actor braucht Verschlüsselung (andere nicht).
- Ein Actor mit großem State profitiert von hohem zstd-Level (andere verwenden gzip).
- Ein Actor mit kleinem State verzichtet auf Kompression.
Überschreibe pro Actor, indem du die Methoden compression() /
encryption() implementierst:
class SensitiveActor extends DurableStateActor<Command, State> { protected encryption() { return { mode: 'client-aes256-gcm' as const, masterKeys: sensitiveKeyRing, info: 'acme/prod/sensitive/v1', }; }
protected compression() { return { algorithm: 'gzip' as const, level: 9 }; }}Das Framework ruft diese bei jedem Persist/Load auf und wendet die zurückgegebene Config an.
Default vs. Override
Abschnitt betitelt „Default vs. Override“// Store-Level-Config — gilt für jeden Actor, sofern nicht überschrieben:const objectStorageDurableStateStoreOptions = ObjectStorageDurableStateStoreOptions.create() .withBackend(backend) .withCompression({ algorithm: 'gzip', level: 6 }) .withEncryption({ mode: 'client-aes256-gcm', masterKeys: defaultKeyRing, info: 'acme/prod/durable-state/v1' });const store = new ObjectStorageDurableStateStore(objectStorageDurableStateStoreOptions);
// Per-Actor: Overrideclass MyActor extends DurableStateActor<...> { protected compression() { return { algorithm: 'none' as const }; } // encryption() nicht überschrieben → verwendet defaultKeyRing}Jede Methode ist unabhängig überschreibbar:
- Eigene Config zurückgeben → diese Config gilt.
- Nicht überschreiben → Default des Stores gilt.
undefinedaus der Methode zurückgeben → ebenfalls Default des Stores.
Use Cases
Abschnitt betitelt „Use Cases“Per-Actor-Verschlüsselung
Abschnitt betitelt „Per-Actor-Verschlüsselung“class CreditCardActor extends DurableStateActor<...> { protected encryption() { // Separater Key Ring UND separater HKDF-Kontext für den PCI-Scope. return { mode: 'client-aes256-gcm' as const, masterKeys: pciKeyRing, info: 'acme/prod/pci/v1', }; }}
class GenericActor extends DurableStateActor<...> { // Kein Override → verwendet Store-Default (keine Verschlüsselung oder generischer keyRing)}PCI / HIPAA / GDPR-Scope: als sensibel klassifizierte Daten verwenden einen separaten Master-Key von nicht-sensiblen. Kompromittierung eines keyRings exponiert den anderen nicht.
Per-Actor-Kompression
Abschnitt betitelt „Per-Actor-Kompression“class ChatTranscriptActor extends DurableStateActor<...> { // Großer Text-State — hohes zstd-Level gewinnt deutlich protected compression() { return { algorithm: 'zstd' as const, level: 19 }; }}
class CounterActor extends DurableStateActor<...> { // Winziger State — Kompressions-Overhead übersteigt Einsparungen protected compression() { return { algorithm: 'none' as const }; }}Passe die Kompression an die Datenform an. Default gzip ist ein sicherer Mittelweg; Per-Actor-Tuning holt mehr raus.
Was beim Persist passiert
Abschnitt betitelt „Was beim Persist passiert“DurableStateActor.persist(state) ↓serializer.toBinary(state) → JSON- oder CBOR-Bytes ↓this.compression() → angewendet, wenn nicht 'none' ↓this.encryption() → angewendet, wenn gesetzt ↓backend.put(key, ciphertext, options)Das Framework schaut bei jedem Persist die Overrides des Actors nach — selbst wenn du die Override-Werte dynamisch änderst (selten), verwenden neue Writes neue Einstellungen.
Was beim Load passiert
Abschnitt betitelt „Was beim Load passiert“DurableStateActor.preStart ↓backend.get(key) → gerahmter ATS1-Body ↓ATS1-Manifest lesen → Encrypted-Flag gesetzt? → entschlüsseln (Key-Version aus dem Manifest-Byte) ↓ATS1-Manifest lesen → Kompressions-Flag → dekomprimieren ↓serializer.fromBinary → StateReads verwenden das selbstbeschreibende ATS1-Manifest im
Body, nicht die aktuellen Overrides des Actors. Ein unter
pciKeyRing verschlüsseltes Objekt wird über pciKeyRing
entschlüsselt, egal ob das aktuelle encryption() des Actors das
sagt oder nicht.
Das bedeutet: das Rotieren einer Actor-Policy bricht alte Daten nicht, solange die notwendigen Keys / Algorithmen verfügbar sind.
Migration beim Ändern von Policies
Abschnitt betitelt „Migration beim Ändern von Policies“// Alt: keine Verschlüsselung.// Neu: Verschlüsselung unter pciKeyRing.class CreditCardActor extends DurableStateActor<...> { protected encryption() { return { mode: 'client-aes256-gcm' as const, masterKeys: pciKeyRing, info: 'acme/prod/pci/v1', }; }}Alte (unverschlüsselte) Reads funktionieren — keine Metadaten → Plaintext-Pfad. Neue Writes verschlüsseln. Schließlich:
- Ein Re-Encryption-Sweep, gefiltert auf die persistenceIds dieses Actors, migriert den Rest.
Siehe Schlüsselrotation.
Mit dem Snapshot-Store arbeiten
Abschnitt betitelt „Mit dem Snapshot-Store arbeiten“class Account extends PersistentActor<Command, Event, State> { // Per-Actor-Kompression für Snapshots: override compression() { return { algorithm: 'zstd' as const }; }
// Per-Actor-Verschlüsselung für Snapshots: override encryption() { return { mode: 'client-aes256-gcm' as const, masterKeys: accountKeyRing, info: 'acme/prod/snapshot/v1', }; }}Gleiche Methoden gelten, wenn der Snapshot-Store durch Object Storage gestützt wird. Persistent Actors und Durable State Actors honorieren beide.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Object Storage im Überblick — das größere Bild.
- Kompression — Algorithmus- + Level-Details.
- Verschlüsselung — Verschlüsselungs-Setup.
- Snapshot-Store-Backend — für Snapshots in Object Storage.
