6.4 KiB
6.4 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cs-eventual-consistency | Eventual Consistency — CAP / Conflict / 보상 | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Eventual Consistency
분산 시스템의 trade-off. CAP: Consistency / Availability / Partition tolerance. Network partition 있을 때 둘 중 하나 포기. 대부분 system 가 partition 에서 availability 우선 = eventually consistent.
📖 핵심 개념
- Strong consistency: 모든 replica 즉시 같은 값.
- Eventual: 시간이 지나면 같아짐.
- Linearizability: 외부 관찰 = 한 노드 처럼.
- Causal consistency: 원인-결과 순서 보장.
💻 핵심 모델
Strong vs Eventual 차이
Strong:
- DB transaction
- Spanner / CockroachDB
- 단일 leader (write)
- Latency / availability 한계
Eventual:
- DNS, CDN, S3 list
- Cassandra, DynamoDB (보통)
- 빠름 / 항상 OK / 큰 scale
- "방금 쓴 게 안 보일 수 있음"
CAP 실제
"Network partition 시 CA 둘 중":
Partition 가 자주 안 일어남 — 그러나 실제 발생.
CP system: ZooKeeper, etcd, MongoDB (default)
AP system: Cassandra, DynamoDB, Couchbase
CA: 단일 노드 (no partition by definition)
PACELC (확장)
Partition 시: A vs C
없을 때 (Else): Latency vs Consistency
ALPS (Cassandra): A + Latency
CALC (Spanner): C + Consistency (어느 때나)
Read-your-writes
사용자가 자기 변경 즉시 봐야 = consistency.
다른 사용자에게는 100ms lag OK.
// Server 가 user 별 sticky → primary
// 또는 last-write timestamp track
async function getUserOrders(userId: string) {
const lastWrite = await redis.get(`recent:${userId}`);
const db = lastWrite && Date.now() - lastWrite < 5000 ? primary : replica;
return db.query('SELECT * FROM orders WHERE user_id = $1', [userId]);
}
Monotonic reads
한 번 새 version 본 후 옛 version 안 보여야.
사용자가 page 1 → page 2 → page 1 = 같은 데이터.
→ Sticky session 또는 client 가 read 결과 cache.
Causal consistency
A → B 라는 관계가 있으면 B 보기 전 A 봐야.
예: Comment 가 post 의존.
Post visible → Comment visible 보장.
→ vector clock / Lamport timestamp.
Conflict resolution (다른 노드 동시 write)
LWW (Last-Write-Wins): 최신 timestamp 선택.
Merge: CRDT — 자동 merge.
Application-defined: user 가 결정 (Dropbox conflict file).
LWW 위험
A: write at 10:00:00.001 (clock skew)
B: write at 10:00:00.000 (real later)
→ A 가 win — but B 가 진짜 마지막.
→ Hybrid Logical Clock 또는 vector clock.
Vector clock
Node A: [A:1, B:0] write x=1
Node B: [A:0, B:1] write x=2 (without seeing A)
→ Concurrent writes — application 이 결정 (또는 둘 다 보존).
CRDT (위 CRDT 문서)
G-Counter, PN-Counter (counter)
LWW-Set, OR-Set (set)
RGA, LSEQ (sequence — text)
→ 자동 merge. Yjs / Automerge.
Quorum
N replicas, write to W, read from R:
W + R > N = read-your-writes (strong).
Cassandra: ONE / QUORUM / ALL.
DynamoDB: eventual / strongly consistent read.
N=3:
W=2, R=2 → quorum (W+R=4 > 3 = strong)
W=1, R=1 → fast 그러나 stale 가능
BASE 원칙
Basically Available
Soft state
Eventually consistent
→ ACID 의 분산 trade-off.
Read repair (Cassandra)
Read 시 여러 replica 비교 → 차이 있으면 가장 최근 winner.
Background 가 다른 replica update.
Anti-entropy / hinted handoff
Anti-entropy: 주기적 replica 비교 (Merkle tree).
Hinted handoff: down 노드 hint 다른 노드 보관 → 복구 시 전달.
→ Cassandra / DynamoDB 자동.
App 측 — 사용자에 stale 표시
const r = await fetch('/api/orders', { headers: { 'consistency': 'eventual' } });
// 응답 헤더
// 'X-Stale-Seconds': 30
if (response.headers.get('X-Stale-Seconds') > 60) {
showWarning('Showing data from 1 minute ago');
}
Write order (causal)
// 1. Post 를 먼저 (commit 보장)
const post = await db.posts.create({...});
// 2. 그 후 알림 보냄 (post 가 visible 후)
await redis.publish('post:created', post.id);
// 다른 service 가 post 안 보일 위험 → 주기적 retry 또는 lookup
Read after write 명시 API
DynamoDB:
GetItem(... ConsistentRead=true) // strongly consistent
GetItem(... ConsistentRead=false) // eventual (default, cheaper)
S3 (2020+ 모든 region):
PUT 후 GET = read-after-write strong (이전 LIST 만 eventual).
Eventual consistency 적합 use case
- DNS (TTL 분 단위)
- CDN (cache invalidation)
- Like / view count (eventual OK)
- Activity feed (몇 초 lag OK)
- Search index (CDC + lag)
부적합
- 결제 (account balance — strong)
- Inventory (stock — strong + lock)
- Booking (seat reservation — strong)
- Auth state (recent password change)
Saga (eventual + 보상)
1. Service A: do X (commit local)
2. Service B: do Y (commit local)
3. 둘 다 fail 가능 — eventually 같은 결과 (compensating tx)
Compensating transaction
Forward: charge → reserve → ship
Failure: reserve fail → refund (compensate)
🤔 의사결정 기준
| 데이터 | 일관성 |
|---|---|
| Money / billing | Strong |
| Inventory | Strong + lock |
| User profile | Read-your-writes |
| Like / view | Eventual |
| Search | Eventual (CDC) |
| Activity feed | Eventual |
| Cache | Eventual + TTL |
❌ 안티패턴
- 모든 거 strong 가정: 비싸 / 느림.
- Eventual + 사용자 자기 거 못 봄: read-your-writes 패턴.
- LWW + clock skew 무시: 데이터 잃음.
- Conflict resolution 없음: 마지막 wins — 데이터 손실.
- Cache TTL 없음: 영원 stale.
- CAP 가정 +항상 partition 없음: 진짜 partition 시 깨짐.
- Quorum 안 자동: app 측 manual.
🤖 LLM 활용 힌트
- 대부분 system = eventual + read-your-writes 보강.
- Strong 은 단일 leader / quorum / Spanner.
- CRDT = 자동 merge.
- Saga + 보상 = 분산 transaction.