ЁЯПл The SchoolтА║ЁЯМР Distributed SystemsтА║ЁЯЧ│я╕П рдзрдбрд╛ 06 тАФ Quorums: N рдкреИрдХреА W рд▓рд╛ рд▓рд┐рд╣рд╛, R рдордзреВрди рд╡рд╛рдЪрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯЧ│я╕П рдзрдбрд╛ 06 тАФ Quorums: N рдкреИрдХреА W рд▓рд╛ рд▓рд┐рд╣рд╛, R рдордзреВрди рд╡рд╛рдЪрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 06 ┬╖ рдорд╛рдЧреЗ: lesson-05-replication ┬╖ рдкреБрдвреЗ: lesson-07-consistency-models


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

рдзрдбреЗ 01тАУ05, рдЕрдзрд┐рдХ quorums рд╕рд╣ leaderless replication: N рдкреНрд░рддреА рдареЗрд╡рд╛, W рдкреНрд░рддреАрдВрдХрдбреЗ write рдкреЛрд╣реЛрдЪрд▓рд╛ рдХреА рддреЛ рдкреВрд░реНрдг рдЭрд╛рд▓рд╛ рдЕрд╕реЗ рдзрд░рд╛, рдЖрдгрд┐ R рдкреНрд░рддреА рд╡рд╛рдЪреВрди рд╕рд░реНрд╡рд╛рдд рдирд╡реА рдареЗрд╡рд╛. рдЬреЗрд╡реНрд╣рд╛ R + W > N, рддреЗрд╡реНрд╣рд╛ рдкреНрд░рддреНрдпреЗрдХ read рд╕рд░реНрд╡рд╛рдд рдирд╡рд╛ write рдкрд╛рд╣рддреЛ. Read repair read рджрд░рдореНрдпрд╛рди рдЬреБрдиреНрдпрд╛ рдкреНрд░рддреА рджреБрд░реБрд╕реНрдд рдХрд░рддреЗ; hinted handoff рдЖрдгрд┐ anti-entropy рддреНрдпрд╛ background рдордзреНрдпреЗ рджреБрд░реБрд╕реНрдд рдХрд░рддрд╛рдд. dist/demo.py рдордзрд▓реЗ quorums() рдЖрдгрд┐ dist/sim.py рдордзрд▓реЗ QuorumStore.

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

рдЖрддрд╛ рдореБрдЦреНрдп рдиреЛрдВрджрд╡рд╣реА рдирд╛рд╣реА. рддреАрди рд╢рд╛рдЦрд╛ рдкреНрд░рддреНрдпреЗрдХреА exam notice рдЪреА рдПрдХ рдкреНрд░рдд рдареЗрд╡рддрд╛рдд (N = 3). рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рддреАрд╡рд░ рдПрдХ version number рдЕрд╕рддреЛ.

рджреАрдкрд┐рдХрд╛ notice "exam on Monday" (version 1) рд╡рд░реВрди "exam moved to Tuesday" (version 2) рдХрд░рддреЗ. рддрд┐рд╕рд▒реНрдпрд╛ рд╢рд╛рдЦреЗрдХрдбрдЪрд╛ рд░рд╕реНрддрд╛ рддреБрдЯрд▓реЗрд▓рд╛ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдлрдХреНрдд рджреЛрди рдкреНрд░рддреАрдВрдирд╛ рдмрджрд▓ рдорд┐рд│рддреЛ (W = 2).

рдирдВрддрд░ рдРрд╢реНрд╡рд░реНрдпрд╛рд▓рд╛ exam рдЪрд╛ рджрд┐рд╡рд╕ рдЬрд╛рдгреВрди рдШреНрдпрд╛рдпрдЪрд╛ рдЖрд╣реЗ. рддреА рдПрдХрд╛ рд╢рд╛рдЦреЗрд╡рд░ рд╡рд┐рд╕рдВрдмрдд рдирд╛рд╣реА, рдореНрд╣рдгреВрди рддреА рддреНрдпрд╛рдкреИрдХреА рджреЛрдШреАрдВрдирд╛ рд╡рд┐рдЪрд╛рд░рддреЗ (R = 2). рдПрдХ рд╕рд╛рдВрдЧрддреЗ "version 2: Tuesday". рдПрдХ рд╕рд╛рдВрдЧрддреЗ "version 1: Monday". рддреА рдореЛрдареА version рдареЗрд╡рддреЗ тАФ Tuesday тАФ рдЖрдгрд┐ рдЬреБрдиреНрдпрд╛ рд╢рд╛рдЦреЗрд▓рд╛ рдирд╡реЗ рдкрд╛рди рдкрд╛рдард╡рддреЗ (read repair).

рдЗрдереЗ рджреЛрдШреАрдВрдирд╛ рд╡рд┐рдЪрд╛рд░рдгреЗ рдиреЗрд╣рдореА рдХрд╛ рдЪрд╛рд▓рддреЗ? 3 рдкреНрд░рддреА рдЖрд╣реЗрдд. рдмрджрд▓ 2 рд╡рд░ рдЖрд╣реЗ. рдРрд╢реНрд╡рд░реНрдпрд╛ 2 рдирд╛ рд╡рд┐рдЪрд╛рд░рддреЗ. 3 рдкреИрдХреА 2-2 рдЪреНрдпрд╛ рджреЛрди рдЧрдЯрд╛рдВрдордзреНрдпреЗ рдХрд┐рдорд╛рди рдПрдХ рд╢рд╛рдЦрд╛ рд╕рд╛рдорд╛рдпрд┐рдХ рдЕрд╕рд▓реАрдЪ рдкрд╛рд╣рд┐рдЬреЗ. рддреНрдпрд╛ рд╕рд╛рдорд╛рдпрд┐рдХ рд╢рд╛рдЦреЗрдХрдбреЗ рдирд╡реЗ рдкрд╛рди рдЖрд╣реЗ. ЁЯОп

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

flowchart LR
    w["тЬНя╕П write v2 'exam moved to Tuesday'<br/>W = 2"] --> c0["copy 0<br/>v2 Tuesday"]
    w --> c1["copy 1<br/>v2 Tuesday"]
    c2["copy 2<br/>v1 Monday тАФ road was cut"]
    r["ЁЯСА read R = 2<br/>asks copies 1 and 2"] --> c1
    r --> c2
    c1 --> k["keep the biggest version: v2<br/>copy 1 is in both groups"]
    k -->|"read repair"| c2

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдкреНрд░рддреНрдпреЗрдХ write рд╕рд╛рдареА рдПрдХрдЪ leader рдЪрд╛рд▓реВ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡рд╛, рдЖрдгрд┐ рдкреЛрд╣реЛрдЪрдгреНрдпрд╛рдЬреЛрдЧрд╛ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡рд╛. Quorums рд╕рд╣, рдХреЛрдгрддреНрдпрд╛рд╣реА W рдкреНрд░рддреА write рдШреЗрдК рд╢рдХрддрд╛рдд рдЖрдгрд┐ рдХреЛрдгрддреНрдпрд╛рд╣реА R рдкреНрд░рддреА read рдЪреЗ рдЙрддреНрддрд░ рджреЗрдК рд╢рдХрддрд╛рдд, рддреНрдпрд╛рдореБрд│реЗ рдПрдХ рдХрд┐рдВрд╡рд╛ рджреЛрди рд╣рд│реВ рдХрд┐рдВрд╡рд╛ рдмрдВрдж рдкрдбрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреА system рдерд╛рдВрдмрд╡рдд рдирд╛рд╣реАрдд. рддреБрдореНрд╣реА рдкреНрд░рддреНрдпреЗрдХ request рд╕рд╛рдареА R рдЖрдгрд┐ W рдирд┐рд╡рдбрддрд╛: рд╕рд░реНрд╡рд╛рдд рдирд╡реЗ рд╣рд╡реЗ рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ рдордЬрдмреВрдд reads, рдереЛрдбрд╛ рдЬреБрдиреЗрдкрдгрд╛ рдЪрд╛рд▓рдд рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ рдЬрд▓рдж reads. Amazon рдЪреНрдпрд╛ Dynamo paper рдиреЗ (2007) рд╣реЗ design рдкреНрд░рд╕рд┐рджреНрдз рдХреЗрд▓реЗ; Cassandra рдЖрдгрд┐ Riak рддреЗ рдкрд╛рд│рддрд╛рдд.

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

dist/sim.py рдордзрд▓реЗ QuorumStore(n) copies рдареЗрд╡рддреЗ, рдореНрд╣рдгрдЬреЗ (version, value) рдЪреА list. write(version, value, w, reachable) reachable рдордзрд▓реНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ w рдкреНрд░рддреАрдВрд╡рд░ рд▓рд┐рд╣рд┐рддреЗ рдЖрдгрд┐ рддреЗ w рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд▓реЗ рдХреА рдирд╛рд╣реА рддреЗ рдкрд░рдд рджреЗрддреЗ. read(r, order, repair=True) order рдордзрд▓реНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ r рдкреНрд░рддреАрдВрдирд╛ рд╡рд┐рдЪрд╛рд░рддреЗ, maximum (version, value) рдареЗрд╡рддреЗ, рдЖрдгрд┐ тАФ repair рдЪрд╛рд▓реВ рдЕрд╕реЗрд▓ рддрд░ тАФ рддреЗ рдЬреБрдиреНрдпрд╛ рдкреНрд░рддреАрдВрд╡рд░ рдкрд░рдд рд▓рд┐рд╣рд┐рддреЗ. рддреЗ (value, stale copy numbers) рдкрд░рдд рджреЗрддреЗ.

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

python3 dist/demo.py quorums
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import QuorumStore
for w in (1, 2, 3):
    for r in (1, 2, 3):
        s = QuorumStore(3); s.write(1, "Monday", 3)
        s.write(2, "Tuesday", w, reachable=[0, 1, 2])      # the write lands on copies 0..w-1
        v, stale = s.read(r, order=[2, 1, 0], repair=False) # the read asks the far end first
        ok = "newest" if v == "Tuesday" else "STALE"
        print(f"W={w} R={r}  R+W={r+w} {'>' if r+w > 3 else 'тЙд'} 3 тЖТ read {v!r:<10} {ok}")
EOF

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

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

тФАтФА 3 copies, the change reached only W=2 of them: [(2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday'), (1, 'exam on Monday')]
   read R=2 from copies 1 and 2 тЖТ 'exam moved to Tuesday' ┬╖ copy [2] was stale and got repaired тЖТ [(2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday')]
   R + W > N (2 + 2 > 3): every read quorum overlaps every write quorum, so the newest version is always seen

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

W=1 R=1  R+W=2 тЙд 3 тЖТ read 'Monday'   STALE
W=1 R=2  R+W=3 тЙд 3 тЖТ read 'Monday'   STALE
W=1 R=3  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=2 R=1  R+W=3 тЙд 3 тЖТ read 'Monday'   STALE
W=2 R=2  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=2 R=3  R+W=5 > 3 тЖТ read 'Tuesday'  newest
W=3 R=1  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=3 R=2  R+W=5 > 3 тЖТ read 'Tuesday'  newest
W=3 R=3  R+W=6 > 3 тЖТ read 'Tuesday'  newest

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

Snippet write рдЖрдгрд┐ read рдкреНрд░рддреАрдВрдЪреНрдпрд╛ рд╡рд┐рд░реБрджреНрдз рдЯреЛрдХрд╛рдВрд╡рд░ рдареЗрд╡рддреЛ тАФ рд╕рд░реНрд╡рд╛рдд рд╡рд╛рдИрдЯ рдкрд░рд┐рд╕реНрдерд┐рддреА. R + W > 3 рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдиреЗ рддрд░реАрд╣реА Tuesday рд╡рд╛рдЪрд▓реЗ; R + W тЙд 3 рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдУрд│реАрдиреЗ Monday рд╡рд╛рдЪрд▓реЗ. рд╣рд╛ рдирд┐рдпрдо рдирд╢рд┐рдмрд╛рд╡рд░ рдирд╛рд╣реА: рддреЛ рдореЛрдЬрдгреАрд╡рд░ рдЖрд╣реЗ. рдЖрдгрд┐ demo рдордзреНрдпреЗ, рдЬреБрдиреА copy 2 read рдиреЗрдЪ рджреБрд░реБрд╕реНрдд рдХреЗрд▓реА.

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

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

Cassandra тАФ consistency level рдореНрд╣рдгрдЬреЗ R рдХрд┐рдВрд╡рд╛ W, рдкреНрд░рддреНрдпреЗрдХ request рд╕рд╛рдареА рдирд┐рд╡рдбрд▓реЗрд▓рд╛. Replication factor 3 рдЕрд╕реЗрд▓ рддрд░ QUORUM рдореНрд╣рдгрдЬреЗ 2. On a real account, cqlsh рдордзреНрдпреЗ:

CREATE KEYSPACE school WITH replication = {'class': 'NetworkTopologyStrategy', 'pune_dc': 3};
CONSISTENCY QUORUM;                      -- this session's reads and writes use QUORUM (2 of 3)
INSERT INTO school.notices (id, body) VALUES ('exam', 'exam moved to Tuesday');
SELECT body FROM school.notices WHERE id = 'exam';
CONSISTENCY ONE;                         -- faster, may be stale

рдЗрддрд░ levels: ALL, LOCAL_QUORUM (рдлрдХреНрдд local data center рдордзрд▓реЗ рдмрд╣реБрдордд тАФ рдЕрдиреЗрдХ data centers рдЕрд╕рддрд╛рдирд╛ рд╕рд╛рдорд╛рдиреНрдп), EACH_QUORUM, ANY (writes рд╕рд╛рдареА; рдПрдХ hint рдкреБрд░реЗрд╕рд╛). Cassandra рдкреНрд░рддреА рддреАрди рдкреНрд░рдХрд╛рд░реЗ рджреБрд░реБрд╕реНрдд рдХрд░рддреЗ: read рджрд░рдореНрдпрд╛рди replicas рдЬреБрд│рдд рдирд╕рддреАрд▓ рддреЗрд╡реНрд╣рд╛ read repair, hinted handoff (рдмрдВрдж replica рд╕рд╛рдареА coordinator hints рдареЗрд╡рддреЛ, рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд max_hint_window рдкрд░реНрдпрдВрдд тАФ default рдиреБрд╕рд╛рд░ 3 рддрд╛рд╕), рдЖрдгрд┐ Merkle trees рд╕рд╣ anti-entropy repair, рдЬреЛ рддреБрдореНрд╣реА рдард░рд▓реЗрд▓реНрдпрд╛ рд╡реЗрд│рд╛рдкрддреНрд░рдХрд╛рд╡рд░ рдЪрд╛рд▓рд╡рддрд╛:

nodetool repair school           # run on each node regularly тАФ within gc_grace_seconds (10 days by default)
nodetool status                  # which nodes are up (UN = up/normal)

Amazon DynamoDB рдкреНрд░рддреНрдпреЗрдХ item рддреАрди Availability Zones рдордзреНрдпреЗ рд╕рд╛рдард╡рддреЗ рдЖрдгрд┐ рд╣реА рдирд┐рд╡рдб R рдЖрдгрд┐ W рдореНрд╣рдгреВрди рдирд╡реНрд╣реЗ, рддрд░ рджреЛрди read modes рдореНрд╣рдгреВрди рджреЗрддреЗ (рдзрдбрд╛ 07). Riak рддреБрдореНрд╣рд╛рд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ bucket рдХрд┐рдВрд╡рд╛ рдкреНрд░рддреНрдпреЗрдХ request рд╕рд╛рдареА r, w рдЖрдгрд┐ n_val рдард░рд╡реВ рджреЗрддреЗ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдкреНрд░рддреНрдпреЗрдХ table рд╕рд╛рдареА рддреБрдореНрд╣реА рд╡рд╛рдкрд░рддрд╛ рддреЗ N, R рдЖрдгрд┐ W рд▓рд┐рд╣рд╛ рдЖрдгрд┐ рдЬрд┐рдереЗ рд╕рд░реНрд╡рд╛рдд рдирд╡реА value рд╣рд╡реА рддрд┐рдереЗ R + W > N рддрдкрд╛рд╕рд╛. nodetool repair (рдХрд┐рдВрд╡рд╛ рддреБрдордЪреНрдпрд╛ store рдЪреЗ repair) рдард░рд▓реЗрд▓реНрдпрд╛ рд╡реЗрд│рд╛рдкрддреНрд░рдХрд╛рд╡рд░ рдареЗрд╡рд╛ рдЖрдгрд┐ рддреЗ рдкреВрд░реНрдг рдЭрд╛рд▓реЗ рдирд╛рд╣реА рддрд░ alert рджреНрдпрд╛.

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

"Reads рд╕рд░реНрд╡рд╛рдд рдирд╡реЗ рдкрд╛рд╣рддрд╛рдд" рд╣реЗ рдПрдХ рд╡рдЪрди рдЖрд╣реЗ. рдЖрдгрдЦреА рдЕрдиреЗрдХ рдЖрд╣реЗрдд, рдЬрд╛рд╕реНрдд рдордЬрдмреВрдд рдЖрдгрд┐ рдХрдордХреБрд╡рдд. рдкреБрдвреЗ: consistency models тАФ рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдХрд╛рдп рд╡рдЪрди рджреЗрддреЛ.

git checkout lesson-07-consistency-models

ЁЯЧ│я╕П Lesson 06 тАФ Quorums: write to W, read from R, out of N

ЁЯУН You are here: Lesson 06 of 12 ┬╖ Previous: lesson-05-replication ┬╖ Next: lesson-07-consistency-models


ЁЯУж What's in this branch

Lessons 01тАУ05, plus leaderless replication with quorums: keep N copies, count a write as done when W copies have it, and read R copies and keep the newest. When R + W > N, every read sees the newest write. Read repair fixes stale copies during a read; hinted handoff and anti-entropy fix them in the background. quorums() in dist/demo.py and QuorumStore in dist/sim.py.

ЁЯзТ Explain like I'm 5

Now there is no main register. Three branches each keep a copy of the exam notice (N = 3). Each copy has a version number on it.

Dipika changes the notice from "exam on Monday" (version 1) to "exam moved to Tuesday" (version 2). The road to the third branch is cut, so only two copies get the change (W = 2).

Later Aishwarya wants to know the exam day. She does not trust one branch, so she asks two of them (R = 2). One says "version 2: Tuesday". One says "version 1: Monday". She keeps the bigger version тАФ Tuesday тАФ and sends the old branch the new page (read repair).

Why does asking two always work here? There are 3 copies. The change is on 2. Aishwarya asks 2. Two groups of 2 out of 3 must share at least one branch. That shared branch has the new page. ЁЯОп

ЁЯЧ║я╕П Diagram

flowchart LR
    w["тЬНя╕П write v2 'exam moved to Tuesday'<br/>W = 2"] --> c0["copy 0<br/>v2 Tuesday"]
    w --> c1["copy 1<br/>v2 Tuesday"]
    c2["copy 2<br/>v1 Monday тАФ road was cut"]
    r["ЁЯСА read R = 2<br/>asks copies 1 and 2"] --> c1
    r --> c2
    c1 --> k["keep the biggest version: v2<br/>copy 1 is in both groups"]
    k -->|"read repair"| c2

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

тЭУ What

ЁЯдФ Why

Because a single leader must be up, and must be reachable, for every write. With quorums, any W copies can take a write and any R copies can answer a read, so one or two slow or dead copies do not stop the system. You choose R and W per request: stronger reads when you need the newest, faster reads when a little staleness is fine. Amazon's Dynamo paper (2007) made this design famous; Cassandra and Riak follow it.

ЁЯФз How (in this repo)

QuorumStore(n) in dist/sim.py keeps copies, a list of (version, value). write(version, value, w, reachable) writes to the first w copies in reachable and returns whether it reached w. read(r, order, repair=True) asks the first r copies in order, keeps the maximum (version, value), and тАФ when repair is on тАФ writes it back to the stale copies. It returns (value, stale copy numbers).

ЁЯзк Try it

python3 dist/demo.py quorums
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import QuorumStore
for w in (1, 2, 3):
    for r in (1, 2, 3):
        s = QuorumStore(3); s.write(1, "Monday", 3)
        s.write(2, "Tuesday", w, reachable=[0, 1, 2])      # the write lands on copies 0..w-1
        v, stale = s.read(r, order=[2, 1, 0], repair=False) # the read asks the far end first
        ok = "newest" if v == "Tuesday" else "STALE"
        print(f"W={w} R={r}  R+W={r+w} {'>' if r+w > 3 else 'тЙд'} 3 тЖТ read {v!r:<10} {ok}")
EOF

тЬЕ Verify тАФ what you should see

quorums prints:

тФАтФА 3 copies, the change reached only W=2 of them: [(2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday'), (1, 'exam on Monday')]
   read R=2 from copies 1 and 2 тЖТ 'exam moved to Tuesday' ┬╖ copy [2] was stale and got repaired тЖТ [(2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday'), (2, 'exam moved to Tuesday')]
   R + W > N (2 + 2 > 3): every read quorum overlaps every write quorum, so the newest version is always seen

Your snippet prints:

W=1 R=1  R+W=2 тЙд 3 тЖТ read 'Monday'   STALE
W=1 R=2  R+W=3 тЙд 3 тЖТ read 'Monday'   STALE
W=1 R=3  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=2 R=1  R+W=3 тЙд 3 тЖТ read 'Monday'   STALE
W=2 R=2  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=2 R=3  R+W=5 > 3 тЖТ read 'Tuesday'  newest
W=3 R=1  R+W=4 > 3 тЖТ read 'Tuesday'  newest
W=3 R=2  R+W=5 > 3 тЖТ read 'Tuesday'  newest
W=3 R=3  R+W=6 > 3 тЖТ read 'Tuesday'  newest

ЁЯПБ What you just proved

The snippet puts the write and the read on opposite ends of the copies тАФ the worst case. Every row with R + W > 3 still read Tuesday; every row with R + W тЙд 3 read Monday. The rule is not luck: it is counting. And in the demo, the stale copy 2 was fixed by the read itself.

тЪая╕П Common mistakes

ЁЯПн In production

Cassandra тАФ the consistency level is R or W, chosen per request. With a replication factor of 3, QUORUM means 2. On a real account, in cqlsh:

CREATE KEYSPACE school WITH replication = {'class': 'NetworkTopologyStrategy', 'pune_dc': 3};
CONSISTENCY QUORUM;                      -- this session's reads and writes use QUORUM (2 of 3)
INSERT INTO school.notices (id, body) VALUES ('exam', 'exam moved to Tuesday');
SELECT body FROM school.notices WHERE id = 'exam';
CONSISTENCY ONE;                         -- faster, may be stale

Other levels: ALL, LOCAL_QUORUM (a majority in the local data center only тАФ common with several data centers), EACH_QUORUM, ANY (for writes; a hint is enough). Cassandra repairs copies three ways: read repair when replicas disagree during a read, hinted handoff (a coordinator keeps hints for a down replica, for up to max_hint_window тАФ 3 hours by default), and anti-entropy repair with Merkle trees, which you run on a schedule:

nodetool repair school           # run on each node regularly тАФ within gc_grace_seconds (10 days by default)
nodetool status                  # which nodes are up (UN = up/normal)

Amazon DynamoDB stores each item in three Availability Zones and exposes the choice as two read modes, not as R and W (lesson 07). Riak lets you set r, w and n_val per bucket or per request.

ЁЯПн Why this matters in production: for each table, write the N, R and W you use and check R + W > N where you need the newest value. Put nodetool repair (or your store's repair) on a schedule and alert when it has not finished.

тПня╕П Next

"Reads see the newest" is one promise. There are several others, stronger and weaker. Next: consistency models тАФ what each one promises the reader.

git checkout lesson-07-consistency-models
тЖР PreviousreplicationNext тЖТconsistency models

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