Durable Storage
Standardmäßig leben DistributedData-Replikas nur im Speicher.
Ein Full-Cluster-Restart (jeder Node gleichzeitig down) verliert
den gesamten State — jeder Key startet wieder bei empty.
Für State, der einen Cold Start überleben muss, gib
DistributedData ein durables Backend mit
DistributedDataOptions.withDurableStore(...):
import { DistributedDataId, DistributedDataOptions, InMemoryDurableStateStore,} from 'actor-ts';
// Any DurableStateStore works — InMemory here; a SQL / object-storage// backend in production (see "Store choices" below).const store = new InMemoryDurableStateStore();
const distributedDataOptions = DistributedDataOptions.create() .withGossipInterval(1_000) .withDurableStore(store);
const dd = system.extension(DistributedDataId).start( cluster, distributedDataOptions,);withDurableStore nimmt direkt einen DurableStateStore — keinen
Wrapper. Intern umwickelt DistributedData ihn mit einem
DurableDistributedDataStore, der anhand der eigenen Replika-ID
dieses Nodes abgelegt wird, also ist Durability pro Replika: jedes
Cluster-Mitglied besitzt einen durablen Datensatz.
Was du damit bekommst:
- Bei
preStart— bevor die Replika Gossip beitritt, lädt sie ihre persistierte Sicht aus dem Store. - Nach jeder Mutation — lokales
update, eingehender Gossip-Merge unddeletespeichern die Sicht neu.
Nach einem Cold Start liefert dd.get(key) den zuletzt
persistierten Wert — nicht undefined — und Gossip merged von dort
über die Replikas hinweg neu.
Was persistiert wird
Abschnitt betitelt „Was persistiert wird“Es gibt keine Key-Whitelist. Die gesamte lokale Sicht der
Replika (jeder Key, den sie hält) wird serialisiert und als ein
durabler Datensatz pro Replika geschrieben, unter der ID
ddata|<replicaId>. Jeder Wert wird über das toJSON() des CRDT
serialisiert.
Weil die gesamte Sicht ein Datensatz ist, schreibt jede Mutation den ganzen Datensatz neu — derselbe Full-State-Rewrite-Trade-off wie bei Durable-State-Actors. Für kleinen, heißen State (Counter, Flags, kleine Sets) ist das billig; für eine große Sicht unter hoher Mutationsrate kostet es Bandbreite und Write-Amplification (siehe die Cautions unten).
Store-Auswahl
Abschnitt betitelt „Store-Auswahl“Jede DurableStateStore-Implementierung
funktioniert — konstruiere sie genau wie für einen Durable-State-Actor
und übergib sie dann an withDurableStore:
| Store | Verwendung |
|---|---|
InMemoryDurableStateStore | Tests / Dev — im Prozess durable, beim Exit weg. |
PostgresDurableStateStore | Cluster-geteilte SQL-Durability. |
MariaDbDurableStateStore | Die SQL-Alternative aus der MySQL-Familie. |
ObjectStorageDurableStateStore | Dateisystem- oder S3-backed Datensätze. |
Siehe Durable State für die Konstruktionsoptionen jedes Stores — DistributedData verwendet dieselben Stores wieder, es gibt also nichts DD-Spezifisches zu konfigurieren.
Per-Node lokal vs. shared Backend
Abschnitt betitelt „Per-Node lokal vs. shared Backend“Der Store ist nach Replika-ID indexiert, die beiden Deployment-Formen unterscheiden sich also darin, wo der Datensatz jeder Replika liegt, nicht darin, was ein Node lädt:
Per-node local store (e.g. object storage at a local path): node-A's store ← ddata|node-A node-B's store ← ddata|node-B cold start → each node loads its own record; gossip reconvergesShared backend (one Postgres / one S3 bucket): shared store ← ddata|node-A, ddata|node-B, … (one record per replica) cold start → each node loads its own record; gossip reconverges- Per-Node lokal — vollständige lokale Recovery, partitions-tolerant; aber der Datensatz eines zerstörten Nodes ist weg (Gossip + Überlebende stellen die Werte wieder her, da CRDTs konvergieren).
- Shared — der Datensatz jeder Replika überlebt an einem Ort, selbst wenn der Node zerstört wird; kostet einen Roundtrip zum Shared Store beim Start und macht diesen Store zu einer geteilten Abhängigkeit.
Für die meisten Apps ist beides okay — CRDT-Konvergenz bedeutet, dass der Verlust des Datensatzes eines Nodes selten fatal ist.
Low-level: DurableDistributedDataStore direkt
Abschnitt betitelt „Low-level: DurableDistributedDataStore direkt“withDurableStore ist der Weg, den du fast immer willst. Der
Wrapper, den es baut, ist auch für sich nutzbar — ein positionaler
Konstruktor über einem DurableStateStore plus einer Replika-ID,
mit load() / save() / clear():
import { DurableDistributedDataStore, GCounter } from 'actor-ts';import { InMemoryDurableStateStore } from 'actor-ts';
const store = new InMemoryDurableStateStore();const durable = new DurableDistributedDataStore(store, 'replica-a');
const view = new Map([['hits', GCounter.empty().increment('replica-a', 5)]]);await durable.save(view); // persist the whole view
const restored = await durable.load(); // Map<string, Crdt> (empty if nothing stored)await durable.clear(); // forget this replica's recordDas ist, was das Framework unter der Haube nutzt; greife nur für Tooling oder Tests direkt darauf zu.
Startup-Ablauf
Abschnitt betitelt „Startup-Ablauf“const distributedDataOptions = DistributedDataOptions.create() .withDurableStore(store);const dd = system.extension(DistributedDataId).start( cluster, distributedDataOptions,);
// On preStart, before joining gossip:// 1. load() this replica's record from the durable store.// 2. decode each entry into its CRDT.// 3. seed the local view.// 4. then join the gossip layer.
const value = dd.get('hits'); // already reflects the persisted viewDer Start ist durch die Lesegeschwindigkeit des Durable Store begrenzt. Für einen lokalen Store mit einer Handvoll Keys sub-millisekündlich; für ein geteiltes SQL- / S3-Backend einstellige Sekunden.
Wann du Durable Storage NICHT einsetzt
Abschnitt betitelt „Wann du Durable Storage NICHT einsetzt“Für At-Rest-Verschlüsselung oder Kompression konfiguriere sie am zugrunde liegenden Store, wo das Backend es unterstützt (z. B. der Object-Storage-Store — siehe Object Storage Encryption).
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Distributed Data im Überblick — das Gesamtbild.
- Replikation — wie State zwischen In-Memory-Replikas propagiert.
- Durable State —
die
DurableStateStore-Backends, die du hier einsteckst. - PersistentActor — die Event-sourced Alternative für State mit häufigen Updates.
