ЁЯПл The SchoolтА║ЁЯМР Distributed SystemsтА║ЁЯТУ рдзрдбрд╛ 04 тАФ Failure detection: рдмрдВрдж рдкрдбрд▓реА рдХреА рдлрдХреНрдд рд╣рд│реВ?
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯТУ рдзрдбрд╛ 04 тАФ Failure detection: рдмрдВрдж рдкрдбрд▓реА рдХреА рдлрдХреНрдд рд╣рд│реВ?

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


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

рдзрдбреЗ 01тАУ03, рдЕрдзрд┐рдХ failure detection: рдкреНрд░рддреНрдпреЗрдХ рд╢рд╛рдЦрд╛ рдард░рд▓реЗрд▓реНрдпрд╛ рд╡реЗрд│рд╛рдкрддреНрд░рдХрд╛рдиреБрд╕рд╛рд░ рдПрдХ рдЫреЛрдЯреА "рдореА рдЪрд╛рд▓реВ рдЖрд╣реЗ" рдЪрд┐рдареНрдареА (рдПрдХ heartbeat) рдкрд╛рдард╡рддреЗ, рдЖрдгрд┐ рд╡реЗрд│реЗрдд heartbeat рдЖрд▓рд╛ рдирд╛рд╣реА рддрд░ рдмрд╛рдХреАрдЪреНрдпрд╛ рддрд┐рдЪреНрдпрд╛рд╡рд░ рд╕рдВрд╢рдп рдШреЗрддрд╛рдд. рдПрдХрдореЗрд╡ knob тАФ timeout тАФ рд▓рд╡рдХрд░ рдХрд│рдгреЗ рдЖрдгрд┐ рдЦреЛрдЯреЗ alarms рдпрд╛рдВрдЪреНрдпрд╛рдд рддрдбрдЬреЛрдб рдХрд░рддреЛ. dist/demo.py рдордзрд▓реЗ detection() рдЖрдгрд┐ dist/sim.py рдордзрд▓реЗ HeartbeatDetector.

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

рдХреЛрд▓реНрд╣рд╛рдкреВрд░ рдкреБрдгреНрдпрд╛рд▓рд╛ рд╕рд╛рдзрд╛рд░рдг рджрд░ 100 ms рд▓рд╛ рдПрдХ postcard ЁЯТМ рдкрд╛рдард╡рдгреНрдпрд╛рдЪреЗ рд╡рдЪрди рджреЗрддреЗ: "рдЕрдЬреВрди рдЙрдШрдбреЗ рдЖрд╣реЗ!"

рдкреБрдгреНрдпрд╛рддрд▓реА рджреАрдкрд┐рдХрд╛ postcards рдпреЗрддрд╛рдирд╛ рдкрд╛рд╣рддреЗ. рд╕рд╣рд╕рд╛ рддреЗ рджрд░ 95 рддреЗ 120 ms рд▓рд╛ рдпреЗрддрд╛рдд. рдкрдг рдХрдзреА рдХрдзреА рдПрдЦрд╛рджреЗ рдЙрд╢рд┐рд░рд╛ рдпреЗрддреЗ тАФ рдПрдХрджрд╛ 340 ms, рдХрд╛рд░рдг рдХреЛрд▓реНрд╣рд╛рдкреВрд░рдЪрд╛ clerk office рд╕рд╛рдл рдХрд░рд╛рдпрд▓рд╛ рдерд╛рдВрдмрд▓рд╛ рд╣реЛрддрд╛ (рдПрдХ рдореЛрдард╛ pause). рдирдВрддрд░ рдПрдХрджрд╛ 180 ms рдЙрд╢реАрд░, рдХрд╛рд░рдг рд░рд╕реНрддреНрдпрд╛рд╡рд░ рдЧрд░реНрджреА рд╣реЛрддреА.

рджреАрдкрд┐рдХрд╛рд▓рд╛ рдПрдХ рдирд┐рдпрдо рдирд┐рд╡рдбрд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ: "X ms рдкрд░реНрдпрдВрдд postcard рдЖрд▓реЗ рдирд╛рд╣реА рддрд░ рдореА рдХреЛрд▓реНрд╣рд╛рдкреВрд░ рдмрдВрдж рдЖрд╣реЗ рдЕрд╕реЗ рдореНрд╣рдгреЗрди".

рдЬрд▓рдж рдЖрдгрд┐ рдХрдзреАрдЪ рди рдЪреБрдХрдгрд╛рд░рд╛ рдЕрд╕рд╛ рдХреЛрдгрддрд╛рдЪ X рдирд╛рд╣реА. рджреАрдкрд┐рдХрд╛ рдлрдХреНрдд рдХреЛрдгрддреА рдЪреВрдХ рддрд┐рд▓рд╛ рдЪрд╛рд▓реЗрд▓ рддреЗ рдирд┐рд╡рдбреВ рд╢рдХрддреЗ.

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

flowchart LR
    k["ЁЯПл Kolhapur<br/>heartbeat every ~100 ms"] -->|"gaps 100 110 95 340 105 120 180 95<br/>then it really dies"| d["ЁЯТУ detector in Pune"]
    d --> t1["timeout 150 ms<br/>2 false alarms<br/>death noticed after 150 ms"]
    d --> t2["timeout 400 ms<br/>0 false alarms<br/>noticed after 400 ms"]
    d --> t3["timeout 800 ms<br/>0 false alarms<br/>noticed after 800 ms"]

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдкреНрд░рддреНрдпреЗрдХ рдЖрдкреЛрдЖрдк рд╣реЛрдгрд╛рд░реА recovery "рддреЛ node рдмрдВрдж рдкрдбрд▓рд╛" рдЗрдереВрди рд╕реБрд░реВ рд╣реЛрддреЗ. Leader рдЦреВрдк рд▓рд╡рдХрд░ рдмрджрд▓рд▓рд╛, рддрд░ GC pause рдореБрд│реЗ failover рд╣реЛрддреЛ тАФ рдХрд┐рдВрд╡рд╛ рджреЛрди leaders (рдзрдбрд╛ 05). рдЦреВрдк рдЙрд╢рд┐рд░рд╛ рдмрджрд▓рд▓рд╛, рддрд░ users рдЦрд▒реНрдпрд╛ outage рдордзреНрдпреЗ рд╡рд╛рдЯ рдкрд╛рд╣рдд рд░рд╛рд╣рддрд╛рдд. рдлреБрдХрдЯрдЪреЗ рдЙрддреНрддрд░ рдирд╛рд╣реА, рдореНрд╣рдгреВрди рддреБрдореНрд╣реА рддреБрдордЪреНрдпрд╛ network рд╕рд╛рдареА timeout рдЬреБрд│рд╡рддрд╛ рдЖрдгрд┐ рд╕рдВрд╢рдпрд╛рд╡рд░реВрди рдШреЗрддрд▓реЗрд▓реА рдкреНрд░рддреНрдпреЗрдХ рдХреГрддреА рдЪреБрдХреВрди рдШрдбрд▓реА рддрд░реА рд╕реБрд░рдХреНрд╖рд┐рдд рдареЗрд╡рддрд╛.

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

dist/sim.py рдордзрд▓реНрдпрд╛ HeartbeatDetector(timeout_ms) рдордзреНрдпреЗ рджреЛрди methods рдЖрд╣реЗрдд. verdicts(gaps_ms) рдкреНрд░рддреНрдпреЗрдХ рдЕрдВрддрд░рд╛рд▓рд╛ 'alive' рдХрд┐рдВрд╡рд╛ 'suspected' (gap > timeout) рдЕрд╕реЗ рдЪрд┐рдиреНрд╣рд╛рдВрдХрд┐рдд рдХрд░рддреЗ. run(gaps_ms) (рдЪрд╛рд▓реВ рдЕрд╕рддрд╛рдирд╛ рдЖрд▓реЗрд▓реЗ рдЦреЛрдЯреЗ alarms, рдЦрд░рд╛ рдореГрддреНрдпреВ рд▓рдХреНрд╖рд╛рдд рдпрд╛рдпрд▓рд╛ рд▓рд╛рдЧрд▓реЗрд▓реЗ ms) рдкрд░рдд рджреЗрддреЗ тАФ рддреЗ timeout рдкреЗрдХреНрд╖рд╛ рдореЛрдареА рдЕрдВрддрд░реЗ рдореЛрдЬрддреЗ, рдЖрдгрд┐ рд▓рдХреНрд╖рд╛рдд рдпрд╛рдпрдЪреА рд╡реЗрд│ рдореНрд╣рдгрдЬреЗ рдлрдХреНрдд timeout рдЪ. dist/demo.py рдордзрд▓реЗ detection() 150, 400 рдЖрдгрд┐ 800 ms рд╡рд╛рдкрд░реВрди рдкрд╛рд╣рддреЗ.

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

python3 dist/demo.py detection
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import HeartbeatDetector
gaps = [100, 110, 95, 340, 105, 120, 180, 95]
for t in (100, 200, 350):
    fa, notice = HeartbeatDetector(t).run(gaps)
    print(f"timeout {t} ms тЖТ false alarms {fa} ┬╖ death noticed after {notice} ms")
print("verdicts at 150 ms:", HeartbeatDetector(150).verdicts(gaps))
busy = [100, 450, 900, 120]            # a very bad day: a long GC pause and a slow road
print("bad day at 400 ms:", HeartbeatDetector(400).verdicts(busy))
EOF

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

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

   timeout 150 ms тЖТ false alarms while alive: 2 ┬╖ the real death is noticed after 150 ms
   timeout 400 ms тЖТ false alarms while alive: 0 ┬╖ the real death is noticed after 400 ms
   timeout 800 ms тЖТ false alarms while alive: 0 ┬╖ the real death is noticed after 800 ms

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

timeout 100 ms тЖТ false alarms 5 ┬╖ death noticed after 100 ms
timeout 200 ms тЖТ false alarms 1 ┬╖ death noticed after 200 ms
timeout 350 ms тЖТ false alarms 0 ┬╖ death noticed after 350 ms
verdicts at 150 ms: ['alive', 'alive', 'alive', 'suspected', 'alive', 'alive', 'suspected', 'alive']
bad day at 400 ms: ['alive', 'suspected', 'suspected', 'alive']

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

рдпрд╛ рдЕрдВрддрд░рд╛рдВрд╕рд╣, snippet рдордзрд▓рд╛ 350 ms рд╣рд╛ рдПрдХрдореЗрд╡ timeout рд╣реЛрддрд╛ рдЬреНрдпрд╛рдд рдПрдХрд╣реА рдЦреЛрдЯрд╛ alarm рдирд╡реНрд╣рддрд╛ тАФ рддреЛ рд╕рд░реНрд╡рд╛рдд рдореЛрдареНрдпрд╛ рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЕрдВрддрд░рд╛рдЪреНрдпрд╛ (340 ms) рдЕрдЧрджреА рд╡рд░ рдЖрд╣реЗ. рдкрдг рд╡рд╛рдИрдЯ рджрд┐рд╡рд╢реА (450 ms рдЖрдгрд┐ 900 ms рдЪреЗ рдЕрдВрддрд░), 400 ms рд╕реБрджреНрдзрд╛ рдЪрд╛рд▓реВ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╢рд╛рдЦреЗрд╡рд░ рджреЛрдирджрд╛ рд╕рдВрд╢рдп рдШреЗрддреЛ. "рдмрд░реЛрдмрд░" timeout рддреБрдордЪреНрдпрд╛ рдирд┐рдпрдВрддреНрд░рдгрд╛рдд рдирд╕рд▓реЗрд▓реНрдпрд╛ network рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЛ. рдореНрд╣рдгреВрдирдЪ detector рдлрдХреНрдд рд╕рдВрд╢рдп рдШреЗрдК рд╢рдХрддреЛ.

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

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

Kubernetes probes тАФ рдкреНрд░рддреНрдпреЗрдХ node рд╡рд░рдЪрд╛ kubelet рдкреНрд░рддреНрдпреЗрдХ container рддрдкрд╛рд╕рддреЛ. рдЕрдпрд╢рд╕реНрд╡реА liveness probe container restart рдХрд░рддреЛ. рдЕрдпрд╢рд╕реНрд╡реА readiness probe рдлрдХреНрдд pod рд▓рд╛ Service рдЪреНрдпрд╛ endpoints рдордзреВрди рдХрд╛рдврддреЛ (traffic рдирд╛рд╣реА), restart рди рдХрд░рддрд╛:

containers:
  - name: timetable
    image: school/timetable:1.4
    livenessProbe:                    # "is the process stuck?" тАФ keep it cheap and local
      httpGet: { path: /livez, port: 8080 }
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 3             # 3 misses in a row тЖТ restart
    readinessProbe:                   # "can it serve now?" тАФ may check its own dependencies
      httpGet: { path: /readyz, port: 8080 }
      periodSeconds: 5
      failureThreshold: 2

Kubernetes node heartbeats тАФ рдкреНрд░рддреНрдпреЗрдХ kubelet kube-node-lease namespace рдордзрд▓рд╛ рдПрдХ Lease object renew рдХрд░рддреЛ (default рдиреБрд╕рд╛рд░ рджрд░ 10 рд╕реЗрдХрдВрджрд╛рдВрдиреА). Node monitor grace period рдЗрддрдХрд╛ рд╡реЗрд│ renewals рдерд╛рдВрдмрд▓реЗ рддрд░ node NotReady рдореНрд╣рдгреВрди рдЪрд┐рдиреНрд╣рд╛рдВрдХрд┐рдд рд╣реЛрддреЛ; рддреНрдпрд╛рд╡рд░рдЪреЗ pods рддреНрдпрд╛рдВрдЪреНрдпрд╛ toleration рдирдВрддрд░ (default рдиреБрд╕рд╛рд░ 300 рд╕реЗрдХрдВрдж) evict рд╣реЛрддрд╛рдд. On a real account:

kubectl get lease -n kube-node-lease
kubectl get nodes

etcd heartbeats тАФ Raft leader (рдзрдбрд╛ 09) рджрд░ --heartbeat-interval рд▓рд╛ (default 100 ms) heartbeat рдкрд╛рдард╡рддреЛ; --election-timeout (default 1000 ms) рдЗрддрдХрд╛ рд╡реЗрд│ рдХрд╛рд╣реАрдЪ рди рдРрдХрд▓реЗрд▓рд╛ follower election рд╕реБрд░реВ рдХрд░рддреЛ. etcd docs рд╕реБрдЪрд╡рддрд╛рдд рдХреА heartbeat interval members рдордзрд▓реНрдпрд╛ round-trip time рдЗрддрдХрд╛ рдЕрд╕рд╛рд╡рд╛, рдЖрдгрд┐ election timeout рддреНрдпрд╛рдЪреНрдпрд╛ рдХрд┐рдорд╛рди 10 рдкрдЯ:

etcd --heartbeat-interval=100 --election-timeout=1000 ...

Cassandra тАФ phi accrual detector, cassandra.yaml рдордзреНрдпреЗ:

phi_convict_threshold: 8      # raise it (for example to 12) on slow or shared networks

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреБрдореНрд╣реА рдЪрд╛рд▓рд╡рддрд╛ рддреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ detector рд╕рд╛рдареА (probes, node leases, database failover, consensus), рддреНрдпрд╛рдЪрд╛ timeout, рддреБрдореНрд╣реА рдкрд╛рд╣рд┐рд▓реЗрд▓рд╛ рд╕рд░реНрд╡рд╛рдд рдореЛрдард╛ рдиреЗрд╣рдореАрдЪрд╛ pause, рдЖрдгрд┐ рдЦреЛрдЯрд╛ alarm рдХреЛрдгрддреА рдХреГрддреА рд╕реБрд░реВ рдХрд░рддреЛ рддреЗ рд▓рд┐рд╣реВрди рдареЗрд╡рд╛. рдордЧ рддреА рдХреГрддреА рд╕реБрд░рдХреНрд╖рд┐рдд рдХрд░рд╛.

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

рднрд╛рдЧ 1 рдкреВрд░реНрдг: рд╣рд░рд╡рд▓реЗрд▓реНрдпрд╛ рдЪрд┐рдареНрдареНрдпрд╛, рдЪреБрдХреАрдЪреА рдШрдбреНрдпрд╛рд│реЗ, рдЕрдЬреНрдЮрд╛рдд рдХреНрд░рдо, рдЕрдирд┐рд╢реНрдЪрд┐рдд рдореГрддреНрдпреВ. рднрд╛рдЧ 2 рдЕрдиреЗрдХ рд╢рд╛рдЦрд╛рдВрдордзреНрдпреЗ рдиреЛрдВрджрд╡рд╣реАрдЪреНрдпрд╛ рдкреНрд░рддреА рдареЗрд╡рддреЛ тАФ рдПрдХ leader рдЖрдгрд┐ рддреНрдпрд╛рдЪреЗ followers рдпрд╛рдВрдкрд╛рд╕реВрди рд╕реБрд░реБрд╡рд╛рдд рдХрд░реВрди.

git checkout lesson-05-replication

ЁЯТУ Lesson 04 тАФ Failure detection: dead or just slow?

ЁЯУН You are here: Lesson 04 of 12 ┬╖ Previous: lesson-03-ordering ┬╖ Next: lesson-05-replication


ЁЯУж What's in this branch

Lessons 01тАУ03, plus failure detection: each branch sends a small "I am alive" note (a heartbeat) on a schedule, and the others suspect it when no heartbeat arrives in time. The one knob тАФ the timeout тАФ trades fast notice against false alarms. detection() in dist/demo.py and HeartbeatDetector in dist/sim.py.

ЁЯзТ Explain like I'm 5

Kolhapur promises to send Pune a postcard ЁЯТМ about every 100 ms: "still open!"

Dipika in Pune watches the postcards arrive. Usually they come every 95 to 120 ms. But sometimes one is late тАФ 340 ms once, because Kolhapur's clerk stopped to clean the office (a long pause). Later one is 180 ms late, because the road was busy.

Dipika must pick a rule: "if no postcard for X ms, I say Kolhapur is closed".

There is no X that is both fast and never wrong. Dipika can only choose which mistake she prefers.

ЁЯЧ║я╕П Diagram

flowchart LR
    k["ЁЯПл Kolhapur<br/>heartbeat every ~100 ms"] -->|"gaps 100 110 95 340 105 120 180 95<br/>then it really dies"| d["ЁЯТУ detector in Pune"]
    d --> t1["timeout 150 ms<br/>2 false alarms<br/>death noticed after 150 ms"]
    d --> t2["timeout 400 ms<br/>0 false alarms<br/>noticed after 400 ms"]
    d --> t3["timeout 800 ms<br/>0 false alarms<br/>noticed after 800 ms"]

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

тЭУ What

ЁЯдФ Why

Because every automatic recovery starts with "that node is dead". Replace a leader too fast, and a GC pause causes a failover тАФ or two leaders (lesson 05). Replace it too slowly, and users wait through a real outage. There is no free answer, so you tune the timeout for your network and make every action taken on suspicion safe to do by mistake.

ЁЯФз How (in this repo)

HeartbeatDetector(timeout_ms) in dist/sim.py has two methods. verdicts(gaps_ms) marks each gap 'alive' or 'suspected' (gap > timeout). run(gaps_ms) returns (false alarms while alive, ms to notice the real death) тАФ it counts gaps above the timeout, and the notice time is simply the timeout itself. detection() in dist/demo.py tries 150, 400 and 800 ms.

ЁЯзк Try it

python3 dist/demo.py detection
python3 - <<'EOF'
import sys; sys.path.insert(0, "dist"); from sim import HeartbeatDetector
gaps = [100, 110, 95, 340, 105, 120, 180, 95]
for t in (100, 200, 350):
    fa, notice = HeartbeatDetector(t).run(gaps)
    print(f"timeout {t} ms тЖТ false alarms {fa} ┬╖ death noticed after {notice} ms")
print("verdicts at 150 ms:", HeartbeatDetector(150).verdicts(gaps))
busy = [100, 450, 900, 120]            # a very bad day: a long GC pause and a slow road
print("bad day at 400 ms:", HeartbeatDetector(400).verdicts(busy))
EOF

тЬЕ Verify тАФ what you should see

detection prints:

   timeout 150 ms тЖТ false alarms while alive: 2 ┬╖ the real death is noticed after 150 ms
   timeout 400 ms тЖТ false alarms while alive: 0 ┬╖ the real death is noticed after 400 ms
   timeout 800 ms тЖТ false alarms while alive: 0 ┬╖ the real death is noticed after 800 ms

Your snippet prints:

timeout 100 ms тЖТ false alarms 5 ┬╖ death noticed after 100 ms
timeout 200 ms тЖТ false alarms 1 ┬╖ death noticed after 200 ms
timeout 350 ms тЖТ false alarms 0 ┬╖ death noticed after 350 ms
verdicts at 150 ms: ['alive', 'alive', 'alive', 'suspected', 'alive', 'alive', 'suspected', 'alive']
bad day at 400 ms: ['alive', 'suspected', 'suspected', 'alive']

ЁЯПБ What you just proved

With these gaps, 350 ms was the only timeout in the snippet with no false alarm тАФ it is just above the biggest normal gap (340 ms). But on a worse day (a 450 ms and a 900 ms gap), even 400 ms suspects a live branch twice. The "right" timeout depends on a network you do not control. That is why a detector can only suspect.

тЪая╕П Common mistakes

ЁЯПн In production

Kubernetes probes тАФ the kubelet on each node checks each container. A failed liveness probe restarts the container. A failed readiness probe only removes the pod from the Service's endpoints (no traffic), without a restart:

containers:
  - name: timetable
    image: school/timetable:1.4
    livenessProbe:                    # "is the process stuck?" тАФ keep it cheap and local
      httpGet: { path: /livez, port: 8080 }
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 3             # 3 misses in a row тЖТ restart
    readinessProbe:                   # "can it serve now?" тАФ may check its own dependencies
      httpGet: { path: /readyz, port: 8080 }
      periodSeconds: 5
      failureThreshold: 2

Kubernetes node heartbeats тАФ each kubelet renews a Lease object in the kube-node-lease namespace (every 10 seconds by default). If the renewals stop for the node monitor grace period, the node is marked NotReady; pods on it are evicted after their toleration (300 seconds by default). On a real account:

kubectl get lease -n kube-node-lease
kubectl get nodes

etcd heartbeats тАФ the Raft leader (lesson 09) sends a heartbeat every --heartbeat-interval (default 100 ms); a follower that hears nothing for --election-timeout (default 1000 ms) starts an election. The etcd docs suggest the heartbeat interval be about the round-trip time between members, and the election timeout at least 10 times that:

etcd --heartbeat-interval=100 --election-timeout=1000 ...

Cassandra тАФ the phi accrual detector, in cassandra.yaml:

phi_convict_threshold: 8      # raise it (for example to 12) on slow or shared networks

ЁЯПн Why this matters in production: for each detector you run (probes, node leases, database failover, consensus), write down its timeout, the biggest normal pause you have seen, and what action a false alarm triggers. Then make that action safe.

тПня╕П Next

Part 1 is done: lost notes, wrong clocks, unknown order, uncertain death. Part 2 keeps copies of the register in several branches тАФ starting with a leader and its followers.

git checkout lesson-05-replication
тЖР PreviousorderingNext тЖТreplication

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