Quorum Reads und Writes
Die Default-Operationen update / get von DistributedData sind
nur lokal — sie werden sofort auf die lokale Replika
angewendet; die Propagation passiert via Gossip. Für Workloads,
die stärkere Garantien brauchen, warten die Varianten
updateAsync und getAsync auf explizite
Replika-Bestätigungen.
Local — der Default
Abschnitt betitelt „Local — der Default“dd.update<GCounter>( 'hits', GCounter.empty, (c) => c.increment(dd.selfReplicaId(), 1),);
const counter = dd.get<GCounter>('hits');Diese Operationen:
update— wende lokal an, fire-and-forget. Kehrt sofort zurück.get— lese die Sicht der lokalen Replika.
Unter der Haube propagiert Gossip Updates über die nächsten Runden an andere Replikas.
Das ist der richtige Default — 99 % der DD-Operationen nutzen diese.
Wann Local nicht reicht
Abschnitt betitelt „Wann Local nicht reicht“Drei häufige Fälle:
- Read-after-Write über Nodes hinweg. Node A schreibt, Node B liest sofort — der Write ist vielleicht noch nicht gegossipt, also sieht Node B veraltete Daten.
- Bestätigter Write. Die App will wissen “mindestens N andere Replikas haben mein Update, bevor ich weitermache” — z. B. bevor dem Client geantwortet wird.
- Strongest-known Read. Wenn Veraltung für diesen Read inakzeptabel ist (Saldo-Check vor einer Zahlung), zwinge den Read, die Mehrheit der Replikas zu konsultieren.
updateAsync — auf Write-Bestätigungen warten
Abschnitt betitelt „updateAsync — auf Write-Bestätigungen warten“await dd.updateAsync<GCounter>( 'hits', GCounter.empty, (c) => c.increment(dd.selfReplicaId(), 1), { consistency: 'majority', timeoutMs: 2_000 },);Wendet lokal an + schickt an die Gossip-Schicht und wartet dann auf Bestätigungen der konfigurierten Anzahl Replikas.
consistency | Worauf gewartet wird |
|---|---|
'local' (Default) | Nur auf sich selbst — das lokale Anwenden ist das ACK. |
'majority' | ⌈N/2 + 1⌉ Replikas haben geACKt. |
'all' | Jede Replika der Up-Member hat geACKt. |
{ kind: 'count'; n: 3 } | Genau n Replikas haben geACKt. |
Das Promise:
- Resolved, wenn genug ACKs innerhalb von
timeoutMseintreffen. - Rejected mit einem Timeout-Fehler, wenn nicht.
Ein Timeout macht den lokalen Write nicht rückgängig — der Wert ist bereits lokal angewendet und gossipt normal weiter. Der Reject signalisiert nur “ich bin mir nicht sicher, ob genug Replikas ihn innerhalb der Deadline gesehen haben”.
getAsync — Lesen mit Konsistenz
Abschnitt betitelt „getAsync — Lesen mit Konsistenz“const counter = await dd.getAsync<GCounter>('hits', { consistency: 'majority' });Dieselben consistency-Optionen. Der Replicator fragt andere
Replikas nach ihrer Sicht, merged die Antworten und gibt den
gemergten Wert zurück.
Das heißt, ein Majority Read sieht mindestens jeden Write, der Majority-bestätigt wurde — den letzten “confirmed”-Wert.
Konsistenz-Level wählen
Abschnitt betitelt „Konsistenz-Level wählen“'majority' ist der Sweet Spot für “wichtige” Reads/Writes —
deckt die meisten Failure-Szenarien zu moderater Latenz ab.
'all' garantiert Konsistenz, scheitert aber, wenn auch nur
eine Replika down (oder langsam) ist — das macht es spröde.
Pragmatische Muster
Abschnitt betitelt „Pragmatische Muster“Majority schreiben + lokal lesen
Abschnitt betitelt „Majority schreiben + lokal lesen“// Mit Majority schreiben — garantiert, dass andere Replikas Bescheid wissenawait dd.updateAsync('counter', ..., { consistency: 'majority' });
// Lokal lesen — schnell, und der Wert spiegelt (eventually) wider,// was andere Writer mit Majority geschrieben habenconst value = dd.get('counter');Das ist das häufige Produktionsmuster. Writes zahlen die Majority-Kosten; Reads sind billig. Der Cluster konvergiert irgendwann.
Majority vor einer kritischen Entscheidung lesen
Abschnitt betitelt „Majority vor einer kritischen Entscheidung lesen“// Majority lesen — den letzten bekannten Wert im Cluster sehenconst balance = await dd.getAsync<PNCounter>('balance', { consistency: 'majority' });
if (balance.value() < 0) { // ... Transaktion ablehnen}Für einmalige Reads, bei denen Veraltung zählt, zahl die Kosten für einen starken Read. Mach nicht jeden Read zu einem Majority Read — das hebelt das Local-First-Design aus.
All-Write für unwiderrufliche Änderungen
Abschnitt betitelt „All-Write für unwiderrufliche Änderungen“await dd.updateAsync('config-locked', ..., { consistency: 'all' });Selten — wenn du brauchst, dass jede Replika die Änderung gesehen hat, bevor es weitergeht. Spröde (jede ausgefallene Replika lässt die Operation scheitern), also wirklich nur für echt-unwiderrufliche Zustandsübergänge.
Timeouts
Abschnitt betitelt „Timeouts“await dd.updateAsync('x', ..., { consistency: 'majority', timeoutMs: 5_000,});Default-Timeout ist gossipIntervalMs × 5 — fünf Gossip-Runden,
typischerweise 5 Sekunden. Pro Call überschreiben, wenn:
- Strengeres Latenz-Budget — niedriger setzen; früh ablehnen, wenn Konsistenz nicht erreicht werden kann.
- Lockerer — bei cross-region Clustern mit hoher Gossip-RTT das Timeout erhöhen.
Bei Timeout wirft das Promise einen Fehler, den du fangen kannst:
try { await dd.updateAsync('x', ..., { consistency: 'majority', timeoutMs: 1_000 });} catch (e) { // Der Write hat lokal funktioniert, aber das Quorum kam nicht rechtzeitig. // Entscheide: erneut versuchen, Fehler nach oben geben, Eventual-Consistency akzeptieren.}Was Quorum nicht garantiert
Abschnitt betitelt „Was Quorum nicht garantiert“Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Distributed Data im Überblick — das Gesamtbild.
- Replikation — wie lokale Writes an Peers propagieren.
- Durable Storage — für State, der Full-Cluster-Restarts überlebt.
- Daten gestalten — wann DDs Eventual Consistency die falsche Wahl ist.