Eventual Consistency
KonsistensEn konsistensmodel hvor systemet garanterer at alle replikaer til sidst vil konvergere til samme værdi hvis ingen nye opdateringer sker.
Beskrivelse
Eventual Consistency er en svag konsistensmodel brugt i distribuerede systemer hvor data replikeres på tværs af flere noder. I modsætning til stærk konsistens (hvor alle noder ser samme data øjeblikkeligt), tillader eventual consistency at forskellige noder midlertidigt kan have forskellige værdier, men garanterer at hvis ingen nye skrivninger sker, vil alle replikaer til sidst konvergere til samme tilstand. Dette er en afvejning defineret i CAP-teoremet - du kan ikke have konsistens, tilgængelighed og partitionstolerance samtidig. Eventual consistency vælger tilgængelighed og partitionstolerance over øjeblikkelig konsistens. Det betyder systemet kan fortsætte med at fungere selv under netværkspartitioner, men læsninger kan returnere forældet data. Eventual consistency er fundamentet for mange NoSQL-databaser (Cassandra, DynamoDB, Riak) og distribuerede caches. Konvergenstid kan variere fra millisekunder til sekunder eller længere afhængigt af systembelastning og netværksforhold. Konfliktløsningsstrategier som Last-Write-Wins, Vector Clocks eller CRDT'er håndterer samtidige opdateringer.
Problem
I distribuerede systemer med replikering, hvordan balancerer vi mellem datakonsistens, systemtilgængelighed og partitionstolerance? Stærk konsistens kræver at alle replikaer synkroniseres før en skrivning returnerer, hvilket giver høj latenstid og reduceret tilgængelighed under netværksproblemer.
Løsning
Eventual consistency lader skrivninger fuldføre hurtigt ved at acceptere at replikaer kan være midlertidigt ude af sync. Systemet garanterer kun at de til sidst vil synkronisere. Dette giver høj tilgængelighed og lav latenstid, perfekt til anvendelser hvor forældede læsninger er acceptable (likes på sociale medier, visningstællere, anbefalinger).
Eksempel
-- DynamoDB eksempel (eventual consistency)
// Default read er eventually consistent
const params = {
TableName: 'Users',
Key: { userId: '123' }
};
// Eventually consistent read (default, hurtigere)
const result = await dynamodb.get(params).promise();
// Kan læse fra enhver replica - måske stale data
// Strongly consistent read (langsommere)
const consistentResult = await dynamodb.get({
...params,
ConsistentRead: true
}).promise();
// Læser fra primary replica - altid fresh data
-- Cassandra eksempel
// Write med tunable consistency
INSERT INTO users (id, name, email)
VALUES (123, 'Peter', 'peter@email.dk')
USING CONSISTENCY QUORUM; -- Vent på majority
INSERT INTO users (id, name, email)
VALUES (124, 'Maria', 'maria@email.dk')
USING CONSISTENCY ONE; -- Kun vent på én replica (eventual)
// Read med tunable consistency
SELECT * FROM users WHERE id = 123
USING CONSISTENCY ALL; -- Læs fra alle replicas (strong)
SELECT * FROM users WHERE id = 123
USING CONSISTENCY ONE; -- Læs fra én replica (eventual)
-- Scenario der viser eventual consistency
// T0: Initial state
// Replica A: { userId: 123, likes: 100 }
// Replica B: { userId: 123, likes: 100 }
// Replica C: { userId: 123, likes: 100 }
// T1: User likes post (write til Replica A)
// Replica A: { userId: 123, likes: 101 } ✓ updated
// Replica B: { userId: 123, likes: 100 } (pending sync)
// Replica C: { userId: 123, likes: 100 } (pending sync)
// Write returns SUCCESS (eventual consistency)
// T2: Anden user læser fra Replica B
// Ser stadig likes: 100 (stale read!)
// T3: Replication completes
// Replica A: { userId: 123, likes: 101 }
// Replica B: { userId: 123, likes: 101 } ✓ synced
// Replica C: { userId: 123, likes: 101 } ✓ synced
// System er nu eventually consistent
// Conflict resolution med Last-Write-Wins
// T1: User A updates name på Replica 1
UPDATE users SET name = 'Peter Hansen', version = 2
WHERE id = 123;
// T1: User B updates name på Replica 2 (concurrent!)
UPDATE users SET name = 'Peter H', version = 2
WHERE id = 123;
// T2: Replication syncer
// Conflict detected! Begge har version 2
// Last-Write-Wins strategy: Brug timestamp
// Hvis User B's write har nyere timestamp, vinder den
// Final: name = 'Peter H' på alle replicas
-- Vector Clocks for conflict detection
const data = {
value: 'Hello',
vectorClock: {
'replica-A': 1,
'replica-B': 0,
'replica-C': 0
}
};
// Concurrent updates vil have forskellige vector clocks
// System kan detektere conflicts og resolve med strategy
-- Shopping cart eksempel (eventual consistency)
// User tilføjer item til cart på forskellige devices
// Phone (Replica A): adds item X
cart = ['item-X'];
// Laptop (Replica B): simultaneously adds item Y
cart = ['item-Y'];
// Eventual sync med merge strategy:
cart = ['item-X', 'item-Y']; // Union af begge
-- Social media example
// Post likes counter
INCREMENT post_likes WHERE post_id = 456;
// Hver replica tæller lokalt
// Eventually syncs til total count
// Slight inaccuracy er acceptable
-- Redis Cluster eksempel
const redis = require('redis');
const cluster = redis.createCluster({
rootNodes: [
{ host: 'node1' },
{ host: 'node2' },
{ host: 'node3' }
]
});
// Writes går til master
await cluster.set('user:123', JSON.stringify(userData));
// Returns success efter master write
// Replicas syncer asynchronously
// Reads kan komme fra replicas
const data = await cluster.get('user:123');
// Might be slightly stale
-- MongoDB replica set
// Write concern
await collection.insertOne(
{ name: 'Peter' },
{ writeConcern: { w: 1 } } // Eventually consistent (return after primary)
);
await collection.insertOne(
{ name: 'Maria' },
{ writeConcern: { w: 'majority' } } // Stronger (wait for majority)
);
// Read preference
await collection.find().readPref('secondary'); // Eventual (read from secondary)
await collection.find().readPref('primary'); // Strong (read from primary)Fordele
- ✓Høj tilgængelighed
- ✓Lav latenstid (skrivninger returnerer hurtigt)
- ✓Partitionstolerance
- ✓Skalerbarhed (mange replikaer)
- ✓Kan fortsætte under netværksproblemer
Udfordringer
- ⚠Forældede læsninger mulige
- ⚠Konfliktløsning nødvendig
- ⚠Applikationer skal håndtere inkonsistens
- ⚠Fejlfinding kan være svært
- ⚠Brugeroplevelsesovervejelser (forældet data)
Anvendelsesområder
- •Sociale medier (likes, følgere, kommentarer)
- •E-handelsproduktkataloger
- •Content delivery networks (CDN)
- •DNS-systemer
- •Indkøbskurve og ønskelister
Eksempler fra den virkelige verden
- •Amazon indkøbskurv (varer synkroniserer til sidst på tværs af enheder)
- •Facebook likes-tæller (endelig optælling er acceptabel)
- •Twitter følgertal
- •CDN-indholdsdistribution
- •DynamoDB globale tabeller