Files
2nd/10_Wiki/Topics/Coding/CS_Eventual_Consistency.md
T
2026-05-09 21:08:02 +09:00

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
cs
distributed
consistency
vibe-coding
language applicable_to
Concept
Backend
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.
// 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)

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.

🔗 관련 문서