ЁЯПл The SchoolтА║ЁЯМР Distributed SystemsтА║ЁЯУП рдзрдбрд╛ 07 тАФ Consistency models: рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдХрд╛рдп рд╡рдЪрди рджреЗрддреЛ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУП рдзрдбрд╛ 07 тАФ Consistency models: рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдХрд╛рдп рд╡рдЪрди рджреЗрддреЛ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 07 ┬╖ рдорд╛рдЧреЗ: lesson-06-quorums ┬╖ рдкреБрдвреЗ: lesson-08-cap-pacelc


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ06, рдЕрдзрд┐рдХ data рдЪреНрдпрд╛ рдкреНрд░рддреА рдЕрд╕рддрд╛рдирд╛ рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдХрд╛рдп рд╡рдЪрди рдорд┐рд│рддреЗ рддреНрдпрд╛рдЪреА рдирд╛рд╡реЗ: linearizable, sequential, causal, read-your-writes, monotonic reads рдЖрдгрд┐ eventual consistency тАФ рд╕рд░реНрд╡рд╛рдд рдордЬрдмреВрдд рдкрд╛рд╕реВрди рд╕рд░реНрд╡рд╛рдд рдХрдордХреБрд╡рдд рдкрд░реНрдпрдВрдд тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХрд╛рдЪреА рдХрд┐рдВрдордд рдХрд╛рдп. dist/demo.py рдордзрд▓реЗ models() рддреНрдпрд╛рдВрдЪреА рдпрд╛рджреА рджреЗрддреЗ; рдЦрд╛рд▓рдЪрд╛ snippet рдзрдбрд╛ 06 рдордзрд▓реЗ QuorumStore рд╡рд╛рдкрд░реВрди рддреНрдпрд╛рдкреИрдХреА рджреЛрди рдЬрд╛рдгреВрдирдмреБрдЬреВрди рдореЛрдбрддреЛ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╕рдЧрд│реЗ рдПрдХрдЪ рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░рддрд╛рдд: "Exam рдХреЛрдгрддреНрдпрд╛ рджрд┐рд╡рд╢реА рдЖрд╣реЗ?" рд╢рд╛рдЦрд╛ рдЙрддреНрддрд░рд╛рдмрджреНрджрд▓ рд╡реЗрдЧрд╡реЗрдЧрд│реА рд╡рдЪрдиреЗ рджреЗрдК рд╢рдХрддрд╛рдд.

рдордЬрдмреВрдд рд╡рдЪрдиреЗ рдЫрд╛рди рдЕрд╕рддрд╛рдд. рддреНрдпрд╛рдВрдирд╛ рдЬрд╛рд╕реНрдд рдерд╛рдВрдмрд╛рд╡реЗ рд▓рд╛рдЧрддреЗ рдЖрдгрд┐ рдЬрд╛рд╕реНрдд рдирд┐рд░реЛрдкреЗ рд▓рд╛рдЧрддрд╛рдд.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    lin["ЁЯеЗ linearizable<br/>one copy, real-time order"] --> seq["ЁЯеИ sequential<br/>one order for all, maybe late"]
    seq --> cau["ЁЯеЙ causal<br/>cause before effect"]
    cau --> ryw["ЁЯЩЛ read-your-writes<br/>you see your own change"]
    cau --> mr["тПй monotonic reads<br/>never back in time"]
    ryw --> ev["ЁЯМля╕П eventual<br/>copies agree when writes stop"]
    mr --> ev
    cost["тмЖя╕П stronger = more coordination<br/>more latency, less availability"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/distributed-systems/lesson-diagrams.html#l07

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг "consistent" рдЪрд╛ рдЕрд░реНрде рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рд▓реЛрдХрд╛рдВрд╕рд╛рдареА рд╡реЗрдЧрд╡реЗрдЧрд│рд╛ рдЕрд╕рддреЛ, рдЖрдгрд┐ bugs рддреНрдпрд╛ рдлрд░рдХрд╛рдд рд░рд╛рд╣рддрд╛рдд. рдПрдЦрд╛рджреА item рджрд╛рдЦрд╡рдгрд╛рд░рд╛, рдордЧ рд▓рдкрд╡рдгрд╛рд░рд╛, рдордЧ рдкреБрдиреНрд╣рд╛ рджрд╛рдЦрд╡рдгрд╛рд░рд╛ cart monotonic reads рдореЛрдбрддреЛ. рдЖрдкрд▓реЗ profile save рдХрд░реВрди рдЬреБрдиреЗрдЪ profile рдкрд╛рд╣рдгрд╛рд░реА user read-your-writes рдореЛрдбрд▓реЗрд▓реЗ рдкрд╛рд╣рддреЗ. рджреЛрдирджрд╛ book рдЭрд╛рд▓реЗрд▓реА seat linearizability рдореЛрдбрддреЗ. рдкреНрд░рддреНрдпреЗрдХ data рдЪреНрдпрд╛ рддреБрдХрдбреНрдпрд╛рд▓рд╛ рдХреЛрдгрддреЗ рд╡рдЪрди рд╣рд╡реЗ рддреЗ рдирд╛рд╡ рджреНрдпрд╛, рдЖрдгрд┐ рдордЬрдмреВрдд рд╡рдЪрдирд╛рдВрдЪреА рдХрд┐рдВрдордд рдлрдХреНрдд рдЬрд┐рдереЗ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ рддрд┐рдереЗрдЪ рдореЛрдЬрд╛.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

dist/demo.py рдордзрд▓реЗ models() "exam рдХреЛрдгрддреНрдпрд╛ рджрд┐рд╡рд╢реА рдЖрд╣реЗ?" рд╕рд╛рдареА рдкрд╛рдЪ рд╡рдЪрдиреЗ print рдХрд░рддреЗ. Snippet рдзрдбрд╛ 06 рдордзрд▓реЗ QuorumStore рд╡рд╛рдкрд░рддреЛ, рдирд┐рд╡рдбрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреАрдВрдХрдбреВрди R = 1 reads рд╕рд╣: рдлрдХреНрдд copy 0 рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд▓реЗрд▓рд╛ рдПрдХ write, рдордЧ copy 0 рдЖрдгрд┐ copy 2 рдХрдбреВрди reads. рдлрдХреНрдд рдЬрд┐рдереЗ рд▓рд┐рд╣рд┐рд▓реЗ рддрд┐рдереВрдирдЪ рд╡рд╛рдЪрд▓реЗ рддрд░ read-your-writes рдорд┐рд│рддреЗ; рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдкреНрд░рддреАрдВрдХрдбреВрди рд╡рд╛рдЪрд▓реЗ рддрд░ monotonic reads рдореЛрдбрддреЗ; repair рд╕рд╣ R = 3 read рд╕рдЧрд│реНрдпрд╛ рдкреНрд░рддреА рдПрдХрддреНрд░ рдЖрдгрддреЛ (convergence).

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 dist/demo.py models
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import QuorumStore
s = QuorumStore(3); s.write(1, "Monday", 3)
s.write(2, "Tuesday", 1, reachable=[0])          # Dipika's change reached only copy 0
print("copies:", s.copies)
print("Dipika reads her own copy 0      тЖТ", s.read(1, order=[0], repair=False)[0], "(read-your-writes holds)")
print("Aishwarya reads copy 2           тЖТ", s.read(1, order=[2], repair=False)[0], "(eventual: stale for now)")
first  = s.read(1, order=[0], repair=False)[0]
second = s.read(1, order=[2], repair=False)[0]
print(f"Katrina reads copy 0, then copy 2 тЖТ {first} then {second} (monotonic reads broken: time went backwards)")
print("R=3 read of all copies           тЖТ", s.read(3, repair=True)[0], "┬╖ after repair:", s.copies)
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

models рд╣реЗ print рдХрд░рддреЗ:

тФАтФА the same question тАФ 'what is the exam day?' тАФ under different promises:
   linearizable      every read sees the latest completed write, as if there were one copy
   sequential        everyone sees the same order, maybe a little late
   causal            if you saw the cause (the change notice), you will see its effect (the new timetable)
   read-your-writes  Dipika always sees the change SHE made, others may lag
   eventual          copies agree when writes stop тАФ meanwhile anything goes
   stronger promises cost coordination (latency, availability) тАФ pick per piece of data

рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:

copies: [(2, 'Tuesday'), (1, 'Monday'), (1, 'Monday')]
Dipika reads her own copy 0      тЖТ Tuesday (read-your-writes holds)
Aishwarya reads copy 2           тЖТ Monday (eventual: stale for now)
Katrina reads copy 0, then copy 2 тЖТ Tuesday then Monday (monotonic reads broken: time went backwards)
R=3 read of all copies           тЖТ Tuesday ┬╖ after repair: [(2, 'Tuesday'), (2, 'Tuesday'), (2, 'Tuesday')]

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреНрдпрд╛рдЪ рддреАрди рдкреНрд░рддреАрдВрдиреА рддреАрди рд╡реЗрдЧрд╡реЗрдЧрд│реЗ рдЕрдиреБрднрд╡ рджрд┐рд▓реЗ. рджреАрдкрд┐рдХрд╛рд▓рд╛ read-your-writes рдорд┐рд│рд╛рд▓реЗ рддреЗ рдлрдХреНрдд рддрд┐рдиреЗ рдЬреНрдпрд╛ рдкреНрд░рддреАрд╡рд░ рд▓рд┐рд╣рд┐рд▓реЗ рддреАрдЪ рд╡рд╛рдЪрд▓реНрдпрд╛рдореБрд│реЗ тАФ рд╣реА рдПрдХ session рдпреБрдХреНрддреА рдЖрд╣реЗ, store рдЪрд╛ рдЧреБрдгрдзрд░реНрдо рдирд╛рд╣реА. рдХрддрд░рд┐рдирд╛рд▓рд╛ рд╡реЗрд│ рдорд╛рдЧреЗ рдЬрд╛рддрд╛рдирд╛ рджрд┐рд╕рд▓реА рдХрд╛рд░рдг рддрд┐рдЪреЗ рджреЛрди reads рджреЛрди рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдкреНрд░рддреАрдВрдХрдбреЗ рдЧреЗрд▓реЗ. рдЖрдгрд┐ store рддрд░реАрд╣реА "eventually consistent" рд╣реЛрддрд╛: repair рд╕рд╣ рдПрдХрд╛ рдкреВрд░реНрдг read рдиреЗ рд╕рдЧрд│реНрдпрд╛ рдкреНрд░рддреА рдЬреБрд│рд╡рд▓реНрдпрд╛.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

Amazon DynamoDB тАФ reads default рдиреБрд╕рд╛рд░ eventually consistent рдЕрд╕рддрд╛рдд. Strongly consistent read рддреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА рдпрд╢рд╕реНрд╡реА рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ write рдЪрд╛ рдкрд░рд┐рдгрд╛рдо рдкрд░рдд рджреЗрддреЛ. On a real account:

aws dynamodb get-item --table-name notices \
    --key '{"id": {"S": "exam"}}' --consistent-read

Strongly consistent read рд▓рд╛ eventually consistent read рдЪреНрдпрд╛ рджреБрдкреНрдкрдЯ read capacity рд▓рд╛рдЧрддреЗ, рдЖрдгрд┐ рддреЗ global secondary indexes рд╡рд░ рдЙрдкрд▓рдмреНрдз рдирд╛рд╣реА. query рдЖрдгрд┐ scan рд╕реБрджреНрдзрд╛ --consistent-read рдШреЗрддрд╛рдд.

PostgreSQL replicas рд╕рд╣ read-your-writes тАФ user рдиреЗ рд▓рд┐рд╣рд┐рд▓реНрдпрд╛рдирдВрддрд░, рдереЛрдбреНрдпрд╛ рд╡реЗрд│рд╛рд╕рд╛рдареА рддрд┐рдЪреЗ reads primary рдХрдбреЗ рдкрд╛рдард╡рд╛; рдХрд┐рдВрд╡рд╛ рддрд┐рдЪреНрдпрд╛ write рдирдВрддрд░ primary рдЪреА WAL position рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛ рдЖрдгрд┐ replica рдиреЗ рддрд┐рдердкрд░реНрдпрдВрдд replay рдХреЗрд▓реНрдпрд╛рд╡рд░рдЪ рддреНрдпрд╛ replica рдХрдбреВрди рд╡рд╛рдЪрд╛:

-- on the primary, after the write:
SELECT pg_current_wal_lsn();          -- for example 0/3000148, kept in her session
-- on a replica, before reading:
SELECT pg_last_wal_replay_lsn() >= '0/3000148'::pg_lsn;   -- true тЖТ safe to read here

Monotonic reads тАФ рдПрдХрд╛ user рдЪреЗ reads рдПрдХрд╛рдЪ replica рд▓рд╛ "sticky" рдХрд░рд╛ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, user ID рдЪреНрдпрд╛ hash рдиреЗ replica рдирд┐рд╡рдбрд╛), рдореНрд╣рдгрдЬреЗ рддреА рдХрдзреАрдЪ рдЬрд╛рд╕реНрдд рдорд╛рдЧреЗ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреАрдХрдбреЗ рдЙрдбреА рдорд╛рд░рдд рдирд╛рд╣реА.

Linearizable reads тАФ etcd reads default рдиреБрд╕рд╛рд░ linearizable рдЕрд╕рддрд╛рдд (leader рдЖрдзреА рдЦрд╛рддреНрд░реА рдХрд░рддреЛ рдХреА рддреЛ рдЕрдЬреВрди leader рдЖрд╣реЗ); etcdctl get --consistency=s local member рдХрдбреВрди рдЬрд▓рдж, рдХрджрд╛рдЪрд┐рдд рдЬреБрдирд╛ serializable read рдорд╛рдЧрддреЗ. QUORUM reads рдЖрдгрд┐ writes рд╕рд╣ Cassandra рд╕рд░реНрд╡рд╛рдд рдирд╡реНрдпрд╛ value рдЪреЗ reads рджреЗрддреЗ (рдзрдбрд╛ 06), рдкрдг рдкреВрд░реНрдг linearizability рдирд╛рд╣реА; рддреНрдпрд╛рд╕рд╛рдареА рддреНрдпрд╛рдЪреЗ lightweight transactions (INSERT ... IF NOT EXISTS) Paxos рд╡рд╛рдкрд░рддрд╛рдд.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреБрдордЪреНрдпрд╛ data рдЪреЗ рдПрдХ table рдмрдирд╡рд╛ тАФ balances, seats, profiles, carts, counters тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдЪрд╛рд▓реВ рд╢рдХреЗрд▓ рдЕрд╕рд╛ рд╕рд░реНрд╡рд╛рдд рдХрдордХреБрд╡рдд model рд▓рд┐рд╣рд╛. рдордЧ store рдЖрдгрд┐ read path рддреЛ рдЦрд░реЛрдЦрд░ рджреЗрддрд╛рдд рдХрд╛ рддреЗ рддрдкрд╛рд╕рд╛.

тПня╕П рдкреБрдвреЗ

рдордЬрдмреВрдд models рд╕рд╛рдареА рдкреНрд░рддреАрдВрдирд╛ рдПрдХрдореЗрдХрд╛рдВрд╢реА рдмреЛрд▓рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. рддреНрдпрд╛рдВрдЪреНрдпрд╛рдордзрд▓рд╛ рд░рд╕реНрддрд╛ рддреБрдЯрд▓рд╛ рддрд░? рдордЧ рддреБрдореНрд╣рд╛рд▓рд╛ рдирд┐рд╡рдбрд╛рд╡реЗ рд▓рд╛рдЧрддреЗ: рдирдХрд╛рд░ рджреНрдпрд╛рдпрдЪрд╛, рдХреА рдХрджрд╛рдЪрд┐рдд рдЬреБрдиреЗ рдЕрд╕рд▓реЗрд▓реЗ рдХрд╛рд╣реАрддрд░реА рдЙрддреНрддрд░ рджреНрдпрд╛рдпрдЪреЗ. CAP рдЖрдгрд┐ PACELC.

git checkout lesson-08-cap-pacelc

ЁЯУП Lesson 07 тАФ Consistency models: what each one promises the reader

ЁЯУН You are here: Lesson 07 of 12 ┬╖ Previous: lesson-06-quorums ┬╖ Next: lesson-08-cap-pacelc


ЁЯУж What's in this branch

Lessons 01тАУ06, plus the names for what a reader is promised when data has copies: linearizable, sequential, causal, read-your-writes, monotonic reads and eventual consistency тАФ from strongest to weakest тАФ and what each one costs. models() in dist/demo.py lists them; the snippet below breaks two of them on purpose with QuorumStore from lesson 06.

ЁЯзТ Explain like I'm 5

Everyone asks the same question: "What is the exam day?" The branches can make different promises about the answer.

Stronger promises are nicer. They also cost more waiting and more messengers.

ЁЯЧ║я╕П Diagram

flowchart TB
    lin["ЁЯеЗ linearizable<br/>one copy, real-time order"] --> seq["ЁЯеИ sequential<br/>one order for all, maybe late"]
    seq --> cau["ЁЯеЙ causal<br/>cause before effect"]
    cau --> ryw["ЁЯЩЛ read-your-writes<br/>you see your own change"]
    cau --> mr["тПй monotonic reads<br/>never back in time"]
    ryw --> ev["ЁЯМля╕П eventual<br/>copies agree when writes stop"]
    mr --> ev
    cost["тмЖя╕П stronger = more coordination<br/>more latency, less availability"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/distributed-systems/lesson-diagrams.html#l07

тЭУ What

ЁЯдФ Why

Because "consistent" means different things to different people, and bugs live in the difference. A cart that shows an item, then hides it, then shows it again breaks monotonic reads. A user who saves her profile and sees the old one breaks read-your-writes. A double-booked seat breaks linearizability. Name the promise each piece of data needs, and pay for the strong ones only where they matter.

ЁЯФз How (in this repo)

models() in dist/demo.py prints the five promises for "what is the exam day?". The snippet uses QuorumStore from lesson 06 with R = 1 reads from chosen copies: a write that reached only copy 0, then reads from copy 0 and copy 2. Reading only where you wrote gives read-your-writes; reading from different copies breaks monotonic reads; an R = 3 read with repair brings all copies together (convergence).

ЁЯзк Try it

python3 dist/demo.py models
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import QuorumStore
s = QuorumStore(3); s.write(1, "Monday", 3)
s.write(2, "Tuesday", 1, reachable=[0])          # Dipika's change reached only copy 0
print("copies:", s.copies)
print("Dipika reads her own copy 0      тЖТ", s.read(1, order=[0], repair=False)[0], "(read-your-writes holds)")
print("Aishwarya reads copy 2           тЖТ", s.read(1, order=[2], repair=False)[0], "(eventual: stale for now)")
first  = s.read(1, order=[0], repair=False)[0]
second = s.read(1, order=[2], repair=False)[0]
print(f"Katrina reads copy 0, then copy 2 тЖТ {first} then {second} (monotonic reads broken: time went backwards)")
print("R=3 read of all copies           тЖТ", s.read(3, repair=True)[0], "┬╖ after repair:", s.copies)
EOF

тЬЕ Verify тАФ what you should see

models prints:

тФАтФА the same question тАФ 'what is the exam day?' тАФ under different promises:
   linearizable      every read sees the latest completed write, as if there were one copy
   sequential        everyone sees the same order, maybe a little late
   causal            if you saw the cause (the change notice), you will see its effect (the new timetable)
   read-your-writes  Dipika always sees the change SHE made, others may lag
   eventual          copies agree when writes stop тАФ meanwhile anything goes
   stronger promises cost coordination (latency, availability) тАФ pick per piece of data

Your snippet prints:

copies: [(2, 'Tuesday'), (1, 'Monday'), (1, 'Monday')]
Dipika reads her own copy 0      тЖТ Tuesday (read-your-writes holds)
Aishwarya reads copy 2           тЖТ Monday (eventual: stale for now)
Katrina reads copy 0, then copy 2 тЖТ Tuesday then Monday (monotonic reads broken: time went backwards)
R=3 read of all copies           тЖТ Tuesday ┬╖ after repair: [(2, 'Tuesday'), (2, 'Tuesday'), (2, 'Tuesday')]

ЁЯПБ What you just proved

The same three copies gave three different experiences. Dipika got read-your-writes only because she read the copy she wrote to тАФ a session trick, not a property of the store. Katrina saw time go backwards because her two reads went to two different copies. And the store was still "eventually consistent": one full read with repair made all copies agree.

тЪая╕П Common mistakes

ЁЯПн In production

Amazon DynamoDB тАФ reads are eventually consistent by default. A strongly consistent read returns the result of every write that succeeded before it. On a real account:

aws dynamodb get-item --table-name notices \
    --key '{"id": {"S": "exam"}}' --consistent-read

A strongly consistent read costs twice the read capacity of an eventually consistent one, and it is not available on global secondary indexes. query and scan take --consistent-read too.

Read-your-writes with PostgreSQL replicas тАФ after a user writes, send her reads to the primary for a short time; or remember the primary's WAL position after her write and read from a replica only once it has replayed that far:

-- on the primary, after the write:
SELECT pg_current_wal_lsn();          -- for example 0/3000148, kept in her session
-- on a replica, before reading:
SELECT pg_last_wal_replay_lsn() >= '0/3000148'::pg_lsn;   -- true тЖТ safe to read here

Monotonic reads тАФ make one user's reads "sticky" to one replica (for example, choose the replica by a hash of the user ID), so she never jumps to a copy that is further behind.

Linearizable reads тАФ etcd reads are linearizable by default (the leader confirms it is still leader first); etcdctl get --consistency=s asks for a faster, possibly stale serializable read from the local member. Cassandra with QUORUM reads and writes gives newest-value reads (lesson 06), but not full linearizability; its lightweight transactions (INSERT ... IF NOT EXISTS) use Paxos for that.

ЁЯПн Why this matters in production: make a table of your data тАФ balances, seats, profiles, carts, counters тАФ and write the weakest model each one can live with. Then check that the store and the read path actually give it.

тПня╕П Next

Stronger models need the copies to talk. What if the road between them is cut? Then you must choose: refuse, or answer something possibly stale. CAP and PACELC.

git checkout lesson-08-cap-pacelc
тЖР PreviousquorumsNext тЖТcap pacelc

This page is the lesson's README from the lesson-07-consistency-models branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.