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

ЁЯУи рдзрдбрд╛ 01 тАФ Partial failure: рдирд┐рд░реЛрдкреНрдпрд╛рдорд╛рд░реНрдлрдд рдмреЛрд▓рдгрд╛рд▒реНрдпрд╛ рд╢рд╛рдЦрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 01 ┬╖ рдкреБрдвреЗ: lesson-02-clocks


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

рд╣рд╛ рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕ рдЬреНрдпрд╛ рдПрдХрд╛ рдкреНрд░рд╢реНрдирд╛рдмрджреНрджрд▓ рдЖрд╣реЗ рддреЛ рдЕрд╕рд╛: рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдЖрддрд╛ рдЪрд╛рд░ рд╢рд╛рдЦрд╛ рдЖрд╣реЗрдд тАФ рдкреБрдгреЗ, рдирд╛рд╢рд┐рдХ, рдирд╛рдЧрдкреВрд░ рдЖрдгрд┐ рдХреЛрд▓реНрд╣рд╛рдкреВрд░. рддреНрдпрд╛ рдПрдХрд╛рдЪ рдиреЛрдВрджрд╡рд╣реАрдЪреНрдпрд╛ рдкреНрд░рддреА рдареЗрд╡рддрд╛рдд (рдЧреБрдг, timetables, рдлреА). рддреНрдпрд╛ рдЦреЛрд▓реАрдЪреНрдпрд╛ рдкрд▓реАрдХрдбреЗ рдУрд░рдбреВрди рдмреЛрд▓реВ рд╢рдХрдд рдирд╛рд╣реАрдд. рддреНрдпрд╛ рдирд┐рд░реЛрдкреНрдпрд╛рдорд╛рд░реНрдлрдд рдЪрд┐рдареНрдареНрдпрд╛ рдкрд╛рдард╡рддрд╛рдд. рдЪрд┐рдареНрдареНрдпрд╛ рд╣рд░рд╡рддрд╛рдд. рдирд┐рд░реЛрдкреЗ рд╣рд│реВ рдЕрд╕рддрд╛рдд. рд░рд╕реНрддреЗ рддреБрдЯрддрд╛рдд. рдПрдЦрд╛рджреА рд╢рд╛рдЦрд╛ рджрд┐рд╡рд╕рднрд░ рдмрдВрдж рд░рд╛рд╣реВ рд╢рдХрддреЗ. рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕рднрд░ рддреБрдореНрд╣реА рд╡рд╛рдкрд░рд╛рд▓ рддреНрдпрд╛ рдЦрд▒реНрдпрд╛ files:

ЁЯОТ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдЖрдзреА: рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд Python 3 рд╣рд╡реЗ, рдмрд╛рдХреА рдХрд╛рд╣реАрдЪ рдирд╛рд╣реА тАФ cloud account рдирд╛рд╣реА, pip install рдирд╛рд╣реА. sim.py рдордзреНрдпреЗ рд╢рд┐рдХрд╡рдгреНрдпрд╛рд╕рд╛рдареАрдЪреЗ models рдЖрд╣реЗрдд, рдЦрд▒реНрдпрд╛ systems рдирд╛рд╣реАрдд. Randomness seeded рдЖрд╣реЗ, рддреНрдпрд╛рдореБрд│реЗ рдкреНрд░рддреНрдпреЗрдХ run рддреАрдЪ рдЧреЛрд╖реНрдЯ print рдХрд░рддреЛ. рдЦрд▒реНрдпрд╛ systems рдореНрд╣рдгрдЬреЗ etcd рдЖрдгрд┐ ZooKeeper (рдПрдХрдордд), Cassandra рдЖрдгрд┐ DynamoDB (quorums), PostgreSQL (replication) рдЖрдгрд┐ Kafka (logs). on a real account рдЕрд╕реЗ рдЪрд┐рдиреНрд╣рд╛рдВрдХрд┐рдд commands рд╕рд╛рдареА рддреНрдпрд╛ systems рдЪрд╛рд▓реВ рдЕрд╕рд╛рд╡реНрдпрд╛ рд▓рд╛рдЧрддрд╛рдд, рдЖрдгрд┐ рддреНрдпрд╛рдВрдирд╛ рдкреИрд╕реЗ рд▓рд╛рдЧреВ рд╢рдХрддрд╛рдд.

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

рдХрддрд░рд┐рдирд╛ рдкреБрдгреЗ office рдордзреНрдпреЗ рдХрд╛рдо рдХрд░рддреЗ. рддрд┐рд▓рд╛ рдЬрд╛рдгреВрди рдШреНрдпрд╛рдпрдЪреЗ рдЖрд╣реЗ: "Exam 10 рд╡рд╛рдЬрддрд╛ рдЖрд╣реЗ рдХрд╛?" рдЙрддреНрддрд░ рдирд╛рд╢рд┐рдХ рдордзреНрдпреЗ рдЖрд╣реЗ. рдореНрд╣рдгреВрди рддреА рдПрдХ рдЪрд┐рдареНрдареА рд▓рд┐рд╣рд┐рддреЗ рдЖрдгрд┐ рдирд┐рд░реЛрдкреНрдпрд╛рдХрдбреЗ рджреЗрддреЗ.

рддреА рд╡рд╛рдЯ рдкрд╛рд╣рддреЗ. рдЙрддреНрддрд░ рдпреЗрдд рдирд╛рд╣реА.

рдХрд╛рдп рдЭрд╛рд▓реЗ? рдХрддрд░рд┐рдирд╛рд▓рд╛ рдХрд│реВ рд╢рдХрдд рдирд╛рд╣реА.

рдкреБрдгреНрдпрд╛рд╣реВрди рдкрд╛рд╣рд┐рд▓реЗ рддрд░ рдЪрд╛рд░рд╣реА рдЧреЛрд╖реНрдЯреА рдЕрдЧрджреА рд╕рд╛рд░рдЦреНрдпрд╛рдЪ рджрд┐рд╕рддрд╛рдд: рд╢рд╛рдВрддрддрд╛. рд╣реЗрдЪ partial failure. рд╢рд╛рд│реЗрдЪрд╛ рдПрдХ рднрд╛рдЧ рдмрд┐рдШрдбрд▓реЗрд▓рд╛ рдЖрд╣реЗ, рдмрд╛рдХреАрдЪреЗ рднрд╛рдЧ рдЕрдЬреВрди рдЪрд╛рд▓реВ рдЖрд╣реЗрдд, рдЖрдгрд┐ рдХреЛрдгрддрд╛ рднрд╛рдЧ рдмрд┐рдШрдбрд▓рд╛ рддреЗ рдХреЛрдгрд╛рд▓рд╛рдЪ рджрд┐рд╕рдд рдирд╛рд╣реА.

рдПрдХрд╛рдЪ campus рд╡рд░ рдЕрд╕реЗ рдХрдзреАрдЪ рд╣реЛрдд рдирд╛рд╣реА. Office рдмрдВрдж рдЕрд╕реЗрд▓ рддрд░ рддреБрдореНрд╣рд╛рд▓рд╛ рдХреБрд▓реВрдк рд▓рд╛рд╡рд▓реЗрд▓реЗ рджрд╛рд░ рджрд┐рд╕рддреЗ. рдЕрдиреЗрдХ campuses рдЕрд╕рд▓реЗ рдХреА рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рд╢рд╛рдВрддрддрд╛ рджрд┐рд╕рддреЗ.

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

flowchart LR
    pune["ЁЯПл Pune<br/>Katrina asks: exam at 10?"]
    nashik["ЁЯПл Nashik<br/>has the answer"]
    pune -->|"note 1: arrives after 5тАУ48 ms"| nashik
    pune -.->|"note 2: lost on the road"| nashik
    nashik -.->|"the answer: lost on the way back"| pune
    x["ЁЯПЪя╕П or Nashik is closed"]
    s["ЁЯРв or the road is just slow"]
    q["тЭУ Pune sees the same thing<br/>every time: silence"]
    pune --- q
    x --- q
    s --- q

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдмрд╣реБрддреЗрдХ рдЦрд▒реНрдпрд╛ systems рдЖрдзреАрдЪ distributed рдЕрд╕рддрд╛рдд: browser рдЖрдгрд┐ API, API рдЖрдгрд┐ database, service рдЖрдгрд┐ queue, рддреАрди рдЗрдорд╛рд░рддреАрдВрдордзреНрдпреЗ рддреАрди database рдкреНрд░рддреА. рджреЛрди boxes рдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рдмрд╛рдг рдореНрд╣рдгрдЬреЗ рд░рд╕реНрддреНрдпрд╛рд╡рд░рдЪрд╛ рдПрдХ рдирд┐рд░реЛрдкреНрдпрд╛. Code рдЬрд░ "рдореА рд╡рд┐рдЪрд╛рд░рд▓реЗ, рдореНрд╣рдгреВрди рдорд▓рд╛ рдЙрддреНрддрд░ рдорд┐рд│реЗрд▓" рдЕрд╕реЗ рдЧреГрд╣реАрдд рдзрд░рдд рдЕрд╕реЗрд▓, рддрд░ рддреЛ рдХрд╛рдпрдо рд╡рд╛рдЯ рдкрд╛рд╣реАрд▓, рдЪреБрдХреАрдЪреНрдпрд╛ рд╡реЗрд│реА retry рдХрд░реЗрд▓, рдХрд┐рдВрд╡рд╛ рддреЗрдЪ payment рджреЛрдирджрд╛ рдХрд░реЗрд▓. рдХреЛрд░реНрд╕рдЪрд╛ рдЙрд░рд▓реЗрд▓рд╛ рднрд╛рдЧ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрдирд╛рд╕рд╛рдареА рд╕рд╛рдзрдиреЗ рджреЗрддреЛ. рдкрд╣рд┐рд▓реА рдкрд╛рдпрд░реА рдореНрд╣рдгрдЬреЗ рд╣реЗ рдорд╛рдиреНрдп рдХрд░рдгреЗ рдХреА рд╢рд╛рдВрддрддреЗрддреВрди рдЬрд╡рд│рдЬрд╡рд│ рдХрд╛рд╣реАрдЪ рдХрд│рдд рдирд╛рд╣реА.

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

dist/sim.py рдордзрд▓реЗ Net(loss, min_ms, max_ms, seed) рдореНрд╣рдгрдЬреЗ рд░рд╕реНрддрд╛. send(a, b) milliseconds рдордзреНрдпреЗ delay рдкрд░рдд рджреЗрддреЗ, рдХрд┐рдВрд╡рд╛ рдЪрд┐рдареНрдареА рдХрдзреАрдЪ рдкреЛрд╣реЛрдЪрдд рдирд╛рд╣реА рддреЗрд╡реНрд╣рд╛ None тАФ рдХрд╛рд░рдг рддреА рд╣рд░рд╡рд▓реА (loss рдореНрд╣рдгрдЬреЗ рддреНрдпрд╛рдЪреА рд╢рдХреНрдпрддрд╛) рдХрд┐рдВрд╡рд╛ рд░рд╕реНрддрд╛ рддреБрдЯрд▓реЗрд▓рд╛ рдЖрд╣реЗ (partition(a, b); heal() рдкреНрд░рддреНрдпреЗрдХ рд░рд╕реНрддрд╛ рджреБрд░реБрд╕реНрдд рдХрд░рддреЗ). рдПрдХ рдЫреЛрдЯрд╛ seeded random generator (Rng) рдкреНрд░рддреНрдпреЗрдХ run рдордзреНрдпреЗ рддреЗрдЪ numbers print рдХрд░рд╛рдпрд▓рд╛ рд▓рд╛рд╡рддреЛ. dist/demo.py рдордзрд▓реЗ failures() 10% loss rate рдиреЗ рдкреБрдгреНрдпрд╛рд╣реВрди рдирд╛рд╢рд┐рдХрд▓рд╛ 20 рдЪрд┐рдареНрдареНрдпрд╛ рдкрд╛рдард╡рддреЗ.

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

python3 dist/demo.py failures
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import Net
for loss in (0.0, 0.1, 0.3, 0.5):
    net = Net(loss=loss, seed=3)
    notes = [net.send("pune", "nashik") for _ in range(20)]
    print(f"loss {loss:.0%} тЖТ arrived {20 - notes.count(None):>2} of 20 ┬╖ lost {notes.count(None)}")
net = Net(seed=3); net.partition("pune", "nashik")
print("road cut  тЖТ puneтЖТnashik:", net.send("pune", "nashik"), "┬╖ puneтЖТnagpur:", net.send("pune", "nagpur"))
net.heal(); print("road healed тЖТ puneтЖТnashik:", net.send("pune", "nashik"), "ms")
EOF
python3 dist/test_dist.py

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

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

тФАтФА Pune sends 20 notes to Nashik by messenger (10% get lost, 5тАУ50 ms each)
   arrived 18 of 20 ┬╖ delays 5тАУ48 ms ┬╖ lost 2

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

loss 0% тЖТ arrived 20 of 20 ┬╖ lost 0
loss 10% тЖТ arrived 18 of 20 ┬╖ lost 2
loss 30% тЖТ arrived 16 of 20 ┬╖ lost 4
loss 50% тЖТ arrived  8 of 20 ┬╖ lost 12
road cut  тЖТ puneтЖТnashik: None ┬╖ puneтЖТnagpur: 15
road healed тЖТ puneтЖТnashik: 16 ms

Tests рд╢реЗрд╡рдЯреА 12/12 passed рджрд╛рдЦрд╡рддрд╛рдд.

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

рд╣рд░рд╡рд▓реЗрд▓реА рдЪрд┐рдареНрдареА рдЖрдгрд┐ рддреБрдЯрд▓реЗрд▓рд╛ рд░рд╕реНрддрд╛ рджреЛрдиреНрд╣реА рдкреБрдгреНрдпрд╛рд▓рд╛ рдПрдХрдЪ рдЙрддреНрддрд░ рджреЗрддрд╛рдд: None. рдкрд╛рдард╡рдгрд╛рд▒реНрдпрд╛рдЪреНрдпрд╛ рдмрд╛рдЬреВрдиреЗ рдХрд╛рд╣реАрдЪ рдлрд░рдХ рдирд╛рд╣реА. рдЖрдгрд┐ рдкреБрдгреЗтАУрдирд╛рд╢рд┐рдХ рд░рд╕реНрддрд╛ рддреБрдЯрд▓реЗрд▓рд╛ рдЕрд╕рддрд╛рдирд╛рд╣реА рдкреБрдгреЗтАУрдирд╛рдЧрдкреВрд░ рд░рд╕реНрддрд╛ рдЕрдЬреВрди рдЪрд╛рд▓рддреЛ (15 ms) тАФ рд╢рд╛рд│рд╛ рдЕрдВрд╢рддрдГ рдмрд┐рдШрдбрд▓реЗрд▓реА рдЖрд╣реЗ, рдкреВрд░реНрдг рдирд╛рд╣реА. рд╣реЗрдЪ partial failure.

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

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

рдкреНрд░рддреНрдпреЗрдХ network call рд▓рд╛ timeout рд╣рд╡рд╛. On a real account тАФ connect timeout рдЖрдгрд┐ total timeout рд╕рд╣ curl:

curl --connect-timeout 2 --max-time 5 https://api.school.example/exam-time

Python рдордзреНрдпреЗ, рддреБрдореНрд╣реА рджрд┐рд▓рд╛ рдирд╛рд╣реА рддрд░ requests рд▓рд╛ рдХреЛрдгрддрд╛рд╣реА timeout рдирд╕рддреЛ:

import requests
r = requests.get("https://api.school.example/exam-time", timeout=(2, 5))  # (connect, read) seconds

gRPC calls рд╕реЛрдмрдд рдПрдХ deadline рдЕрд╕рддреЗ рдЬреА request рдмрд░реЛрдмрд░ рдкреНрд░рд╡рд╛рд╕ рдХрд░рддреЗ, рддреНрдпрд╛рдореБрд│реЗ рд╡рд╛рдЯреЗрд╡рд░рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ service рд▓рд╛ рдХрд┐рддреА рд╡реЗрд│ рдЙрд░рд▓рд╛ рдЖрд╣реЗ рддреЗ рдХрд│рддреЗ:

stub.GetExamTime(request, timeout=5.0)   # the call fails with DEADLINE_EXCEEDED after 5 s

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреБрдордЪреНрдпрд╛ services рдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рдмрд╛рдг рдХрд╛рдврд╛. рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рд▓рд┐рд╣рд╛: рддреНрдпрд╛рдЪрд╛ timeout, рд╢рд╛рдВрддрддрд╛ рдЕрд╕рд▓реНрдпрд╛рд╕ рдХрд╛рдп рд╣реЛрддреЗ, рдЖрдгрд┐ call рдкреБрдиреНрд╣рд╛ рдХрд░рдгреЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдЖрд╣реЗ рдХрд╛. рдЬреНрдпрд╛ рдмрд╛рдгрд╛рд▓рд╛ рдЙрддреНрддрд░реЗ рдирд╛рд╣реАрдд, рддрд┐рдереВрдирдЪ рдкреБрдврдЪреЗ outage рд╕реБрд░реВ рд╣реЛрддреЗ.

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

рд╢рд╛рдВрддрддрд╛ рд╣рд╛ рдПрдХ рдкреНрд░рд╢реНрди. рд╡реЗрд│ рд╣рд╛ рдкреБрдврдЪрд╛: рдкреНрд░рддреНрдпреЗрдХ рд╢рд╛рдЦреЗрдЪреЗ рд╕реНрд╡рддрдГрдЪреЗ рдШрдбреНрдпрд╛рд│ рдЕрд╕рддреЗ, рдЖрдгрд┐ рдХреЛрдгрддреАрд╣реА рджреЛрди рдШрдбреНрдпрд╛рд│реЗ рдЬреБрд│рдд рдирд╛рд╣реАрдд.

git checkout lesson-02-clocks

ЁЯУи Lesson 01 тАФ Partial failure: branches that talk by messenger

ЁЯУН You are here: Lesson 01 of 12 ┬╖ Next: lesson-02-clocks


ЁЯУж What's in this branch

The one problem this whole course is about: the school now has four branch campuses тАФ Pune, Nashik, Nagpur and Kolhapur. They keep copies of one register (grades, timetables, fees). They cannot shout across the room. They send notes by messenger. Notes get lost. Messengers are slow. Roads get cut. A branch can close for the day. Real files you will use all the way through:

ЁЯОТ Before you start: you need Python 3 and nothing else тАФ no cloud account, no pip install. sim.py holds teaching models, not real systems. Randomness is seeded, so every run prints the same story. The real systems are etcd and ZooKeeper (agreement), Cassandra and DynamoDB (quorums), PostgreSQL (replication) and Kafka (logs). Commands marked on a real account need those systems running, and they can cost money.

ЁЯзТ Explain like I'm 5

Katrina works in the Pune office. She needs to know: "Is the exam at 10?" The answer is in Nashik. So she writes a note and gives it to a messenger.

She waits. No answer comes.

What happened? Katrina cannot know.

From Pune, all four look exactly the same: silence. That is partial failure. One part of the school is broken, the other parts still work, and nobody can see which part.

On one campus this never happens. If the office is closed, you see the locked door. With many campuses, you only see silence.

ЁЯЧ║я╕П Diagram

flowchart LR
    pune["ЁЯПл Pune<br/>Katrina asks: exam at 10?"]
    nashik["ЁЯПл Nashik<br/>has the answer"]
    pune -->|"note 1: arrives after 5тАУ48 ms"| nashik
    pune -.->|"note 2: lost on the road"| nashik
    nashik -.->|"the answer: lost on the way back"| pune
    x["ЁЯПЪя╕П or Nashik is closed"]
    s["ЁЯРв or the road is just slow"]
    q["тЭУ Pune sees the same thing<br/>every time: silence"]
    pune --- q
    x --- q
    s --- q

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

тЭУ What

ЁЯдФ Why

Because most real systems are already distributed: a browser and an API, an API and a database, a service and a queue, three database copies in three buildings. Every arrow between two boxes is a messenger on a road. If the code assumes "I asked, so I will get an answer", it will wait forever, retry at the wrong time, or do the same payment twice. The rest of the course gives you tools for each problem. The first step is to accept that silence tells you almost nothing.

ЁЯФз How (in this repo)

Net(loss, min_ms, max_ms, seed) in dist/sim.py is the road. send(a, b) returns a delay in milliseconds, or None when the note never arrives тАФ because it was lost (loss is the chance of that) or because the road is cut (partition(a, b); heal() repairs every road). A tiny seeded random generator (Rng) makes every run print the same numbers. failures() in dist/demo.py sends 20 notes from Pune to Nashik with a 10% loss rate.

ЁЯзк Try it

python3 dist/demo.py failures
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import Net
for loss in (0.0, 0.1, 0.3, 0.5):
    net = Net(loss=loss, seed=3)
    notes = [net.send("pune", "nashik") for _ in range(20)]
    print(f"loss {loss:.0%} тЖТ arrived {20 - notes.count(None):>2} of 20 ┬╖ lost {notes.count(None)}")
net = Net(seed=3); net.partition("pune", "nashik")
print("road cut  тЖТ puneтЖТnashik:", net.send("pune", "nashik"), "┬╖ puneтЖТnagpur:", net.send("pune", "nagpur"))
net.heal(); print("road healed тЖТ puneтЖТnashik:", net.send("pune", "nashik"), "ms")
EOF
python3 dist/test_dist.py

тЬЕ Verify тАФ what you should see

failures prints:

тФАтФА Pune sends 20 notes to Nashik by messenger (10% get lost, 5тАУ50 ms each)
   arrived 18 of 20 ┬╖ delays 5тАУ48 ms ┬╖ lost 2

Your snippet prints:

loss 0% тЖТ arrived 20 of 20 ┬╖ lost 0
loss 10% тЖТ arrived 18 of 20 ┬╖ lost 2
loss 30% тЖТ arrived 16 of 20 ┬╖ lost 4
loss 50% тЖТ arrived  8 of 20 ┬╖ lost 12
road cut  тЖТ puneтЖТnashik: None ┬╖ puneтЖТnagpur: 15
road healed тЖТ puneтЖТnashik: 16 ms

The tests end with 12/12 passed.

ЁЯПБ What you just proved

A lost note and a cut road both give Pune the same answer: None. From the sender's side there is no difference. And while the PuneтАУNashik road is cut, the PuneтАУNagpur road still works (15 ms) тАФ the school is partly broken, not fully broken. That is partial failure.

тЪая╕П Common mistakes

ЁЯПн In production

Every network call needs a timeout. On a real account тАФ curl with a connect timeout and a total timeout:

curl --connect-timeout 2 --max-time 5 https://api.school.example/exam-time

In Python, requests has no timeout unless you give one:

import requests
r = requests.get("https://api.school.example/exam-time", timeout=(2, 5))  # (connect, read) seconds

gRPC calls carry a deadline that travels with the request, so every service on the path knows how much time is left:

stub.GetExamTime(request, timeout=5.0)   # the call fails with DEADLINE_EXCEEDED after 5 s

ЁЯПн Why this matters in production: draw every arrow between your services. For each one, write: its timeout, what happens on silence, and whether the call is safe to repeat. An arrow with no answers is where the next outage starts.

тПня╕П Next

Silence is one problem. Time is the next one: each branch has its own clock, and no two clocks agree.

git checkout lesson-02-clocks
тЖР Course homeall lessonsNext тЖТclocks

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