---
id: cs-eventual-consistency
title: Eventual Consistency — CAP / Conflict / 보상
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [cs, distributed, consistency, vibe-coding]
tech_stack: { language: "Concept", applicable_to: ["Backend"] }
applied_in: []
aliases: [CAP theorem, eventual consistency, strong consistency, linearizability, BASE]
---

# 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.
```

```ts
// 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 표시
```tsx
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)
```ts
// 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)
```

→ [[Backend_Saga_Patterns]].

### 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.

## 🔗 관련 문서
- [[Backend_Saga_Patterns]]
- [[Backend_Geo_Replication]]
- [[CS_CRDT_Patterns]]
