@@ -114,15 +114,76 @@ Consul KV:
114114A common production setting is ` ttl=20, loop_wait=5, retry_timeout=5 `
115115for ~ 10–15s RTO. Don't go below ` ttl >= 2 * loop_wait ` (Patroni rejects).
116116
117+ ## Hard-kill variant (whole-userspace failure)
118+
119+ Same outline, but instead of stopping just ` dstack-patroni-1 ` , simulate
120+ a "host crashed but recovered" scenario by killing all containers on
121+ the leader at once:
122+
123+ ``` bash
124+ ssh ... root@${LEADER} -22.${GW} " docker stop -t 0 \$ (docker ps -q)"
125+ ```
126+
127+ This kills patroni, postgres, mesh-conn, consul, sidecar, webdemo, and
128+ the keepalive — everything that produces signal for the rest of the
129+ cluster. Bring the host back via:
130+
131+ ``` bash
132+ ssh ... root@${LEADER} -22.${GW} \
133+ " cd /tapp && docker compose --env-file /dstack/.host-shared/.decrypted-env \
134+ -p dstack -f /tapp/docker-compose.yaml up -d"
135+ ```
136+
137+ ` docker compose up -d ` respects the dependency order
138+ (bootstrap-secrets → mesh-conn → consul → patroni).
139+
140+ ### Measured timeline (run from 2026-05-03)
141+
142+ ```
143+ T_kill 07:26:42 docker stop -t 0 ALL 7 containers on worker-4
144+ T_new_leader 07:27:13 worker-3 promoted (timeline 16 → 17) +31s
145+ T_first_write 07:27:15 INSERT succeeds on worker-3 +33s ← RTO
146+ T_restart_W4 07:27:46 docker compose up -d on worker-4
147+ T_W4_rejoined 07:28:34 worker-4 streaming, lag=0 +48s after restart
148+ ```
149+
150+ ** Hard-kill RTO ≈ 33 seconds** , ~ 9 seconds longer than the soft-kill
151+ above. That extra cost is Consul gossip-failure detection: with
152+ soft-kill only the Patroni leader-key TTL expires, while with hard-kill
153+ the entire Consul agent is gone, so the surviving peers see * both*
154+ signals.
155+
156+ ### Things confirmed by the hard-kill that the soft-kill didn't exercise
157+
158+ - ** Best-replica selection under uneven lag.** Going into the kill,
159+ worker-3 was timeline=16, lag=0 while worker-5 was timeline=15 with
160+ measurable lag. Patroni picked worker-3 (the up-to-date one), not
161+ the alphabetically-earlier one. The promote-best-replica heuristic
162+ works.
163+ - ** mesh-conn QUIC ICE redial after a peer's userspace evaporates.**
164+ Other peers' QUIC links to worker-4 hit ` MaxIdleTimeout=60s ` and
165+ tore down; once worker-4's containers came back, the new mesh-conn
166+ established fresh ICE pairs and replication resumed without
167+ intervention. The earlier yamux build had a pathology where
168+ redial-after-stress would loop forever; QUIC is clean.
169+ - ** Cheap rejoin survives hard-kill.** worker-4's pgdata was
170+ untouched (the kernel never died, just userspace), so on bring-up
171+ Patroni replayed local WAL and joined as a streaming replica on the
172+ new timeline. No pg_basebackup, no multi-MB re-copy through
173+ mesh-conn.
174+
117175## What this demo does NOT cover
118176
119- * ** Hard CVM kill** (whole VM down, not just the patroni container).
120- RTO should be similar but exercises dstack/CVM-level recovery; mesh-conn
121- ICE will need to redial when the old CVM comes back.
177+ * ** CVM reboot or kernel panic** — ` reboot ` /` poweroff ` from inside
178+ the CVM. This involves the dstack platform's CVM lifecycle and is
179+ qualitatively different from container-level kills. Consider
180+ separately if/when you need to claim "host hardware failure"
181+ resilience.
122182* ** Network partition** : split-brain isolation between coordinators
123183 vs workers. Patroni + Consul should handle it, but worth a separate
124184 test before claiming partition-tolerance.
125185* ** Disk loss on rejoin** : if the ex-leader's pgdata is wiped, rejoin
126186 WILL trigger a full pg_basebackup through mesh-conn. The
127- ~ 25 MB/s throughput and the QUIC transport mean that even a 10 GB
128- rebuild takes ~ 7 minutes (acceptable), but it's a different code path.
187+ ~ 25 MB/s throughput and the QUIC transport mean even a 10 GB
188+ rebuild takes ~ 7 minutes (acceptable), but it's a different code
189+ path than the cheap rejoin shown above.
0 commit comments