A Map of Consistency Models — Linearizable, Serializable, Causal, Eventual TODO
Concept
A consistency model is a contract for how much reordering a system allows under concurrent access. Linearizability requires that each individual operation on a single object appear to take effect atomically at some point between its call and its response, in an order that respects real time. Serializability is a property of multi-object transactions: the result of concurrent execution just has to match some sequential execution — it doesn't require respecting real-time order. Causal consistency guarantees ordering only for writes that causally precede one another; concurrent writes may be seen in different orders on different nodes. Eventual consistency promises only that replicas converge at some point once updates stop arriving. Strict serializability, combining the two strongest properties, is the strongest of all — and the stronger the guarantee, the higher the cost in availability and latency, making this fundamentally a CAP/PACELC trade-off.
Bugs of the form "I read right after I wrote, but the value isn't there" usually come from a system actually being weaker than the model people assumed it guaranteed; choosing an unnecessarily strong model, conversely, just wastes latency and cost for nothing.
Code & Formula
# 일관성 모델 지도 — 선형화(linearizable) 읽기와 최종 일관성(eventual) 읽기를 토이 복제 카운터로 비교.
# 선형화는 항상 최신 쓰기를 즉시 보고, 최종 일관성은 복제 지연 동안 stale read가 가능하다.
import random
random.seed(2)
class LinearizableCounter:
"""단일 리더에게만 쓰고 읽는다 -> 항상 최신 값 (선형화 가능)"""
def __init__(self):
self.value = 0
def write(self, delta):
self.value += delta
def read(self):
return self.value
class EventuallyConsistentCounter:
"""리더에 쓰고, 복제본은 비동기로 지연 복제 -> replica read는 stale할 수 있다"""
def __init__(self, replication_lag=2):
self.leader_value = 0
self.replica_value = 0
self.pending = [] # (도착까지 남은 tick, delta)
self.replication_lag = replication_lag
def write(self, delta):
self.leader_value += delta
self.pending.append([self.replication_lag, delta])
def tick(self):
"""시간 한 틱 진행: 복제 지연이 다 된 갱신을 replica에 반영"""
still_pending = []
for entry in self.pending:
entry[0] -= 1
if entry[0] <= 0:
self.replica_value += entry[1]
else:
still_pending.append(entry)
self.pending = still_pending
def read_from_replica(self):
return self.replica_value
lin = LinearizableCounter()
ec = EventuallyConsistentCounter(replication_lag=2)
lin.write(+1)
ec.write(+1)
print("write-then-read 직후:")
print(f" linearizable read = {lin.read()} (항상 최신)")
print(f" eventual read = {ec.read_from_replica()} (아직 복제 안 됨 -> stale)")
for t in range(1, 3):
ec.tick()
print(f" tick {t} 후 eventual read = {ec.read_from_replica()}")
print("\n결론: 최종 일관성은 갱신이 멈추면 언젠가 수렴하지만, 그 사이엔 stale read를 감수해야 한다.")
docs/code/algorithms/algorithms-52.py
Exercise
Reproduce a stale read for real by running a write-then-read scenario against a read replica with replication lag, then fix it to get read-your-writes via session pinning or reading from the leader.
Practical Connection
A blockchain's finalized chain effectively provides a global linearizable order, but pre-finality reorganization is possible, so an indexer or off-chain order book should treat pre-finality state as only eventually consistent and reflect it in settlement only after finality.
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.