This is the multi-page printable view of this section. .
Failure Model
Patroni failures have 10 categories by failure target. Detection paths group them into five categories, described here.
| # | Failure Scenario | Description | Final Path |
|---|---|---|---|
| 1 | PG process crash | crash, OOM killed | Active Detection |
| 2 | PG connection refused | max_connections | Active Detection |
| 3 | PG zombie | Process alive but unresponsive | Active Detection (timeout) |
| 4 | Patroni process crash | kill -9, OOM | Passive Detection |
| 5 | Patroni zombie | Process alive but stuck | Watchdog |
| 6 | Node down | Power outage, hardware failure | Passive Detection |
| 7 | Node zombie | IO hang, CPU starvation | Watchdog |
| 8 | Primary ↔ DCS network failure | Firewall, switch failure | Network Partition |
| 9 | Storage failure | Disk failure, disk full, mount failure | Active Detection or Watchdog |
| 10 | Manual switchover | Switchover/Failover | Manual Trigger |
For RTO calculations, all failures follow two paths. This section calculates their minimum, maximum, and average recovery times.
- Passive election triggered after Patroni loses contact
- Patroni actively detects failure and triggers switchover
flowchart LR
A([Primary Failure]) --> B{Patroni<br/>Detected?}
B -->|PG Crash| C[Attempt Local Restart]
B -->|Node Down| D[Wait TTL Expiration]
C -->|Success| E([Local Recovery])
C -->|Fail/Timeout| F[Release Leader Lock]
D --> F
F --> G[Replica Election]
G --> H[Execute Promote]
H --> I[HAProxy Detects]
I --> J([Service Restored])
style A fill:#dc3545,stroke:#b02a37,color:#fff
style E fill:#198754,stroke:#146c43,color:#fff
style J fill:#198754,stroke:#146c43,color:#fff1 - Model of Patroni Passive Failure
infographic list-row-simple-horizontal-arrow
data
desc Lease Expiration Stages
items
- label Lease Expiration
- label Replica Detect
- label Elect & Promote
- label Haproxy Up
theme light
palette antvRTO Timeline
tooltip: { trigger: axis, axisPointer: { type: shadow }, formatter: $fn:fmt }
legend: { top: 0, itemGap: 12, data: [Lease Expiration, Replica Detection, Lock Contest & Promote, Health Check] }
grid: { left: 64, right: 24, bottom: 32, top: 40 }
xAxis: { type: value, name: Seconds, nameLocation: end, max: 160, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: true, lineStyle: { type: dashed, opacity: 0.5 } }, minorTick: { show: true, splitNumber: 5 }, minorSplitLine: { show: true, lineStyle: { type: dotted, opacity: 0.2 } } }
yAxis: { type: category, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: false }, axisLabel: { fontSize: 10, fontFamily: monospace }, data: [wide-max, wide-avg, wide-min, "", safe-max, safe-avg, safe-min, "", norm-max, norm-avg, norm-min, "", fast-max, fast-avg, fast-min] }
series:
- { name: Lease Expire, type: bar, stack: main, barWidth: 20, z: 2, emphasis: { focus: series }, itemStyle: { color: "#e15759" }, data: [120, 110, 100, "-", 60, 55, 50, "-", 30, 27, 25, "-", 20, 17, 15] }
- { name: Replica Detect, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#edc949" }, data: [20, 10, 0, "-", 10, 5, 0, "-", 5, 3, 0, "-", 5, 3, 0] }
- { name: Elect & Promote, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#59a14f" }, data: [2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0] }
- { name: HAProxy Check, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#4e79a7" }, data: [8, 6, 4, "-", 6, 5, 3, "-", 4, 3, 2, "-", 2, 2, 1] }
- { name: Total RTO, type: bar, barGap: "-100%", barWidth: 20, z: 1, itemStyle: { color: "#888", opacity: 0 }, emphasis: { itemStyle: { opacity: 0 } }, data: [150, 127, 104, "-", 78, 66, 53, "-", 41, 34, 27, "-", 29, 23, 16] }
- { name: RTO Budget, type: bar, barGap: "-100%", barWidth: 20, z: 0, itemStyle: { color: "rgba(0,0,0,0.08)" }, emphasis: { itemStyle: { color: "rgba(0,0,0,0.12)" } }, data: [150, 150, 150, "-", 90, 90, 90, "-", 45, 45, 45, "-", 30, 30, 30] }Failure Model
| Phase | Best | Worst | Average | Description |
|---|---|---|---|---|
| Lease Expiration | ttl - loop | ttl | ttl - loop/2 | Best: crash just before refresh Worst: crash right after refresh |
| Replica Detect | 0 | loop | loop / 2 | Best: exactly at check point Worst: just missed check point |
| Election Promote | 0 | 2 | 1 | Best: direct lock and promote Worst: API timeout + Promote |
| HAProxy Check | (rise-1) × fastinter | (rise-1) × fastinter + inter | (rise-1) × fastinter + inter/2 | Best: state change before check Worst: state change right after check |
Key Difference Between Passive and Active Failover:
| Scenario | Patroni Status | Lease Handling | Primary Wait Time |
|---|---|---|---|
| Active Failover (PG crash) | Alive, healthy | Actively tries to restart PG, releases lease on timeout | primary_start_timeout |
| Passive Failover (Node crash) | Dies with node | Cannot actively release, must wait for TTL expiration | ttl |
During passive failure, Patroni stops with the node and cannot release the Leader Key. An election waits until the DCS lease TTL expires.
Timeline Analysis
Phase 1: Lease Expiration
Primary Patroni renews the Leader Key each loop_wait cycle and resets its TTL.
- Best case: Failure occurs just before lease refresh (elapsed
loopsince last refresh), remaining TTL =ttl - loop - Worst case: Failure occurs right after lease refresh, must wait full
ttl - Average case:
ttl - loop/2
Phase 2: Replica Detection
Each loop_wait cycle, replicas check the DCS Leader Key.
- Best case: Replica happens to wake when lease expires, wait
0 - Worst case: Replica just entered sleep when lease expires, wait
loop - Average case:
loop/2
Phase 3: Lock Contest & Promote
After detecting Leader Key expiration, replicas begin an election. The winner acquires the key and runs pg_ctl promote.
- Query replica replication positions in parallel through REST APIs. Typical response time is 10ms; the hardcoded timeout is 2s.
- Compare WAL positions and select the best candidate. Replicas attempt atomic CAS creation of the Leader Key.
- Run
pg_ctl promote. Its duration is usually negligible.
- Best case: Single replica or immediate lock acquisition and promotion, constant overhead
0.1s - Worst case: DCS API call timeout:
2s - Average case:
1sconstant overhead
Phase 4: Health Check
HAProxy requires rise consecutive successful checks before routing traffic to the new primary.
- Best case: New primary promoted just before check,
(rise-1) × fastinter - Worst case: New primary promoted right after check,
(rise-1) × fastinter + inter - Average case:
(rise-1) × fastinter + inter/2
RTO Formula
Add the phase durations to calculate total RTO:
Best Case
Average Case
Worst Case
Model Calculation
Apply the four RTO presets to these formulas:
Four Mode Calculation Results (unit: seconds, format: min / avg / max)
| Phase | fast | norm | safe | wide |
|---|---|---|---|---|
| Lease Expiration | 15 / 17 / 20 | 25 / 27 / 30 | 50 / 55 / 60 | 100 / 110 / 120 |
| Replica Detection | 0 / 3 / 5 | 0 / 3 / 5 | 0 / 5 / 10 | 0 / 10 / 20 |
| Lock Contest & Promote | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 |
| Health Check | 1 / 2 / 2 | 2 / 3 / 4 | 3 / 5 / 6 | 4 / 6 / 8 |
| Total | 16 / 23 / 29 | 27 / 34 / 41 | 53 / 66 / 78 | 104 / 127 / 150 |
2 - Model of Patroni Active Failure
infographic list-row-simple-horizontal-arrow
data
desc When Patroni is healthy but PostgreSQL crashes
items
- label Crash Found
- label Restart Timeout
- label Replica Detect
- label Elect Promote
- label HAProxy Check
theme light
palette antvRTO Timeline
tooltip: { trigger: axis, axisPointer: { type: shadow }, formatter: $fn:fmt }
legend: { top: 0, itemGap: 12, data: [ Crash Found, Restart Timeout, Replica Detection, Elect Promote, HAProxy Check] }
grid: { left: 64, right: 24, bottom: 32, top: 40 }
xAxis: { type: value, name: Seconds, nameLocation: end, max: 160, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: true, lineStyle: { type: dashed, opacity: 0.5 } }, minorTick: { show: true, splitNumber: 5 }, minorSplitLine: { show: true, lineStyle: { type: dotted, opacity: 0.2 } } }
yAxis: { type: category, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: false }, axisLabel: { fontSize: 10, fontFamily: monospace }, data: [wide-max, wide-avg, wide-min, "", safe-max, safe-avg, safe-min, "", norm-max, norm-avg, norm-min, "", fast-max, fast-avg, fast-min] }
series:
- { name: Crash Found, type: bar, stack: main, barWidth: 20, z: 2, emphasis: { focus: series }, itemStyle: { color: "#b07aa1" }, data: [20, 10, 0, "-", 10, 5, 0, "-", 5, 3, 0, "-", 5, 3, 0] }
- { name: Restart Timeout, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#f28e2c" }, data: [95, 95, 0, "-", 45, 45, 0, "-", 25, 25, 0, "-", 15, 15, 0] }
- { name: Replica Detect, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#edc949" }, data: [20, 10, 0, "-", 10, 5, 0, "-", 5, 3, 0, "-", 5, 3, 0] }
- { name: Elect Promote, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#59a14f" }, data: [2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0] }
- { name: HAProxy Check, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#4e79a7" }, data: [8, 6, 4, "-", 6, 5, 3, "-", 4, 3, 2, "-", 2, 2, 1] }
- { name: RTO Total, type: bar, barGap: "-100%", barWidth: 20, z: 1, itemStyle: { color: "#888", opacity: 0 }, emphasis: { itemStyle: { opacity: 0 } }, data: [145, 122, 4, "-", 73, 61, 3, "-", 41, 35, 2, "-", 29, 24, 1] }
- { name: RTO Budget, type: bar, barGap: "-100%", barWidth: 20, z: 0, itemStyle: { color: "rgba(0,0,0,0.08)" }, emphasis: { itemStyle: { color: "rgba(0,0,0,0.12)" } }, data: [150, 150, 150, "-", 90, 90, 90, "-", 45, 45, 45, "-", 30, 30, 30] }Failure Model
| Item | Best | Worst | Average | Description |
|---|---|---|---|---|
| Crash Found | 0 | loop | loop/2 | Best: PG crashes right before check Worst: PG crashes right after check |
| Restart Timeout | 0 | start | start | Best: PG recovers instantly Worst: Wait full start timeout before releasing lease |
| Replica Detect | 0 | loop | loop/2 | Best: Right at check point Worst: Just missed check point |
| Elect Promote | 0 | 2 | 1 | Best: Acquire lock and promote directly Worst: API timeout + Promote |
| HAProxy Check | (rise-1) × fastinter | (rise-1) × fastinter + inter | (rise-1) × fastinter + inter/2 | Best: State changes before check Worst: State changes right after check |
Key Difference Between Active and Passive Failure:
| Scenario | Patroni Status | Lease Handling | Main Wait Time |
|---|---|---|---|
| Active Failure (PG crash) | Alive, healthy | Actively tries to restart PG, releases lease after timeout | primary_start_timeout |
| Passive Failure (node down) | Dies with node | Cannot actively release, must wait for TTL expiry | ttl |
During active failure, Patroni remains available. It detects a PostgreSQL crash and attempts a restart. If restart succeeds, service recovers locally. If recovery times out, Patroni releases the Leader Key and triggers an election.
Timing Analysis
Phase 1: Failure Detection
Each loop_wait cycle, Patroni checks PostgreSQL through pg_isready or a process check.
- Best case: PG crashes right before Patroni check, detected immediately, wait
0 - Worst case: PG crashes right after check, wait for next cycle, wait
loop - Average case:
loop/2
Phase 2: Restart Timeout
After detecting a crash, Patroni attempts to restart PostgreSQL. There are two possible results:
Path A: Self-healing Success (Best case)
- PostgreSQL restarts and service recovers.
- No failover occurs, so RTO is very short.
- Wait:
0relative to the failover path.
Path B: Failover Required (Average/Worst case)
- PostgreSQL does not recover before
primary_start_timeout. - Patroni releases the Leader Key.
- Wait:
start.
Note: The average assumes failover is necessary. A quick local restart can substantially reduce RTO.
Phase 3: Standby Detection
Standbys check the DCS Leader Key each loop_wait cycle. After the primary releases it, they detect its absence and begin an election.
- Best case: Standby wakes right when lease is released, wait
0 - Worst case: Standby just went to sleep when lease released, wait
loop - Average case:
loop/2
Phase 4: Lock & Promote
When the Leader Key is absent, standbys begin an election. The winner acquires the key and runs pg_ctl promote.
- Query standby replication positions in parallel through REST APIs. Typical response time is 10ms; the hardcoded timeout is 2s.
- Compare WAL positions and select the best candidate. Standbys attempt atomic CAS creation of the Leader Key.
- Run
pg_ctl promote. Its duration is usually negligible.
- Best case: Single standby or direct lock acquisition and promote, constant overhead
0.1s - Worst case: DCS API call timeout:
2s - Average case:
1sconstant overhead
Phase 5: Health Check
HAProxy requires rise consecutive successful checks before it marks the new primary healthy.
- Best case: New primary comes up right at check time,
(rise-1) × fastinter - Worst case: New primary comes up right after check,
(rise-1) × fastinter + inter - Average case:
(rise-1) × fastinter + inter/2
RTO Formula
Add the phase durations to calculate total RTO:
Best Case (PG instant self-healing)
Average Case (Failover required)
Worst Case
Model Calculation
Apply the four RTO presets to these formulas:
Calculation Results for Four Modes (unit: seconds, format: min / avg / max)
| Phase | fast | norm | safe | wide |
|---|---|---|---|---|
| Failure Detection | 0 / 3 / 5 | 0 / 3 / 5 | 0 / 5 / 10 | 0 / 10 / 20 |
| Restart Timeout | 0 / 15 / 15 | 0 / 25 / 25 | 0 / 45 / 45 | 0 / 95 / 95 |
| Standby Detection | 0 / 3 / 5 | 0 / 3 / 5 | 0 / 5 / 10 | 0 / 10 / 20 |
| Lock & Promote | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 |
| Health Check | 1 / 2 / 2 | 2 / 3 / 4 | 3 / 5 / 6 | 4 / 6 / 8 |
| Total | 1 / 24 / 29 | 2 / 35 / 41 | 3 / 61 / 73 | 4 / 122 / 145 |
Comparison with Passive Failure
| Phase | Active Failure (PG crash) | Passive Failure (node down) | Description |
|---|---|---|---|
| Detection Mechanism | Patroni active detection | TTL passive expiry | Active detection discovers failure faster |
| Core Wait | start | ttl | start is usually less than ttl, but requires additional failure detection time |
| Lease Handling | Active release | Passive expiry | Active release is more timely |
| Self-healing Possible | Yes | No | Active detection can attempt local recovery |
RTO Comparison (Average case):
| Mode | Active Failure (PG crash) | Passive Failure (node down) | Difference |
|---|---|---|---|
| fast | 24s | 23s | +1s |
| norm | 35s | 34s | +1s |
| safe | 61s | 66s | -5s |
| wide | 122s | 127s | -5s |
Analysis: In
fastandnorm, active failures wait forprimary_start_timeout(start), making RTO slightly longer than for passive failures. Insafeandwide,start < ttl - loop, so active recovery is faster. A successful local restart can make the best-case active RTO much shorter.
3 - Network Partition
infographic list-row-simple-horizontal-arrow
data
title Network Partition Failover Flow
desc Primary partitioned from DCS, Patroni proactively demotes to prevent split-brain, waits for TTL expiration before switchover
items
- label Primary Demote
desc Patroni demotes PG after retry timeout
icon mingcute/shield-fill
- label Lease Expiration
desc Leader Key TTL expires
icon mingcute/close-circle-fill
- label Replica Detection
desc Replica detects lease expiration, starts election
icon mingcute/key-2-fill
- label Lock & Promote
desc Replica acquires lock and promotes to new primary
icon mingcute/radar-fill
- label Health Check
desc HAProxy detects new primary online
icon mingcute/arrow-up-circle-fill
theme light
palette antvRTO Timeline
tooltip: { trigger: axis, axisPointer: { type: shadow }, formatter: $fn:fmt }
legend: { top: 0, itemGap: 12, data: [Primary Demote, Lease Expiration, Replica Detection, Lock & Promote, Health Check] }
grid: { left: 64, right: 24, bottom: 32, top: 40 }
xAxis: { type: value, name: sec, nameLocation: end, max: 160, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: true, lineStyle: { type: dashed, opacity: 0.5 } }, minorTick: { show: true, splitNumber: 5 }, minorSplitLine: { show: true, lineStyle: { type: dotted, opacity: 0.2 } } }
yAxis: { type: category, axisLine: { show: true }, axisTick: { show: true }, splitLine: { show: false }, axisLabel: { fontSize: 10, fontFamily: monospace }, data: [wide-max, wide-avg, wide-min, "", safe-max, safe-avg, safe-min, "", norm-max, norm-avg, norm-min, "", fast-max, fast-avg, fast-min] }
series:
- { name: Primary Demote, type: bar, stack: main, barWidth: 20, z: 2, emphasis: { focus: series }, itemStyle: { color: "#76b7b2" }, data: [50, 40, 30, "-", 30, 25, 20, "-", 15, 13, 10, "-", 10, 8, 5] }
- { name: Lease Expiration, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#e15759" }, data: [70, 70, 70, "-", 30, 30, 30, "-", 15, 15, 15, "-", 10, 10, 10] }
- { name: Replica Detection, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#edc949" }, data: [20, 10, 0, "-", 10, 5, 0, "-", 5, 3, 0, "-", 5, 3, 0] }
- { name: Lock & Promote, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#59a14f" }, data: [2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0, "-", 2, 1, 0] }
- { name: Health Check, type: bar, stack: main, z: 2, emphasis: { focus: series }, itemStyle: { color: "#4e79a7" }, data: [8, 6, 4, "-", 6, 5, 3, "-", 4, 3, 2, "-", 2, 2, 1] }
- { name: RTO Total, type: bar, barGap: "-100%", barWidth: 20, z: 1, itemStyle: { color: "#888", opacity: 0 }, emphasis: { itemStyle: { opacity: 0 } }, data: [150, 127, 104, "-", 78, 66, 53, "-", 41, 34, 27, "-", 29, 23, 16] }
- { name: RTO Budget, type: bar, barGap: "-100%", barWidth: 20, z: 0, itemStyle: { color: "rgba(0,0,0,0.08)" }, emphasis: { itemStyle: { color: "rgba(0,0,0,0.12)" } }, data: [150, 150, 150, "-", 90, 90, 90, "-", 45, 45, 45, "-", 30, 30, 30] }Failure Model
| Phase | Best | Worst | Average | Notes |
|---|---|---|---|---|
| Demote | retry | loop + retry | loop/2 + retry | Patroni retries after detecting partition, demotes after timeout |
| Lease Expiration | ttl - loop - retry | ttl - loop - retry | ttl - loop - retry | Remaining TTL time after demotion (approximately constant) |
| Replica Detection | 0 | loop | loop/2 | Best: Right at detection point Worst: Just missed detection |
| Lock & Promote | 0 | 2 | 1 | Best: Direct lock and promote Worst: API timeout + Promote |
| Health Check | (rise-1) × fastinter | (rise-1) × fastinter + inter | (rise-1) × fastinter + inter/2 | Best: State changes before check Worst: State changes right after check |
Key difference between network partition and node crash:
| Scenario | Patroni State | PostgreSQL State | Lease Handling | Split-brain Risk |
|---|---|---|---|---|
| Node Crash (Expire) | Dies with node | Completely unavailable | Passive wait for TTL expiration | None |
| Network Partition (This scenario) | Alive but cannot access DCS | May still be running (needs active demotion) | Passive wait for TTL expiration | Yes, needs protection |
During a network partition, the primary can continue accepting writes, creating split-brain risk. If Patroni cannot renew the Leader Key, it actively demotes PostgreSQL to read-only or shuts it down.
Timeline Analysis
Phase 1: Primary Demotion
If a network partition blocks DCS access, primary Patroni retries its failed Leader Key renewal.
- Detection: The next
loop_waitcycle detects the partition. - Retry: Patroni retries DCS operations during
retry_timeout. - Demotion: After retries time out, Patroni demotes PostgreSQL to prevent split brain.
Design constraint: loop_wait + 2 × retry_timeout ≤ ttl lets the primary demote before the lease expires.
Phase 2: Lease Expiration
After demotion, the Leader Key remains in DCS until its TTL expires.
After demotion, the cluster waits for the remaining TTL. Partition detection and remaining TTL are negatively correlated in this model. An earlier partition means slower detection but more remaining TTL. Their sum is constant:
Note: Demotion plus lease expiration takes approximately ttl, as with an expired lease after node failure.
Phase 3: Replica Detection
Each loop_wait cycle, a replica checks the DCS Leader Key.
- Best case: Replica wakes right when lease expires, wait
0 - Worst case: Replica just entered sleep when lease expires, wait
loop - Average case:
loop/2
Phase 4: Lock & Promote
After detecting Leader Key expiration, replicas begin an election.
- Best case: Single replica or directly acquires lock and promotes,
≈ 0 - Worst case: DCS API call timeout,
2s - Average case:
1s
Phase 5: Health Check
HAProxy requires rise consecutive successful checks before it marks the new primary healthy.
- Best case:
(rise-1) × fastinter - Worst case:
(rise-1) × fastinter + inter - Average case:
(rise-1) × fastinter + inter/2
RTO Formula
Add the phase durations to calculate total RTO.
Demotion plus lease expiration is approximately ttl. Thus, the network-partition RTO formula matches the expired-lease failure formula:
Best Case
Average Case
Worst Case
Model Calculation
Apply the four RTO presets to these formulas:
Patroni constraint validation (loop + 2×retry ≤ ttl):
| Mode | loop | retry | TTL | loop + 2×retry | Meets constraint? |
|---|---|---|---|---|---|
| fast | 5 | 5 | 20s | 15s | ✓ Safe |
| norm | 5 | 10 | 30s | 25s | ✓ Safe |
| safe | 10 | 20 | 60s | 50s | ✓ Safe |
| wide | 20 | 30 | 120s | 80s | ✓ Safe |
Four mode calculation results (seconds, format: min / avg / max)
| Phase | fast | norm | safe | wide |
|---|---|---|---|---|
| Primary Demote | 5 / 8 / 10 | 10 / 13 / 15 | 20 / 25 / 30 | 30 / 40 / 50 |
| Lease Expiration | 10 | 15 | 30 | 70 |
| Replica Detection | 0 / 3 / 5 | 0 / 3 / 5 | 0 / 5 / 10 | 0 / 10 / 20 |
| Lock & Promote | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 | 0 / 1 / 2 |
| Health Check | 1 / 2 / 2 | 2 / 3 / 4 | 3 / 5 / 6 | 4 / 6 / 8 |
| Total | 16 / 23 / 29 | 27 / 34 / 41 | 53 / 66 / 78 | 104 / 127 / 150 |
Network partitions and node crashes have the same RTO in this model because both wait for TTL expiration.
Split-brain Protection
Split brain is the main partition risk: the old primary might still accept writes. Patroni provides these protections:
1. Primary Self-Demotion
If Patroni cannot renew the Leader Key, it actively demotes PostgreSQL.
2. Linux Watchdog
If Patroni hangs and cannot demote PostgreSQL, Linux watchdog forces a system restart.
3. Fencing Mechanism
Configure fencing scripts to isolate the old primary forcibly, for example by disabling its network interface or stopping services.
Special Scenarios
Scenario A: Primary partitioned from DCS, replicas normal
The following is the most common partition scenario and the main subject of this page.
- Primary Patroni cannot refresh Leader Key → Active demotion
- Replica normally detects TTL expiration → Elected as new primary
- RTO ≈ Expire failure RTO
Scenario B: Primary normal, replica partitioned from DCS
- Primary normally refreshes Leader Key
- Replica cannot participate in election (but replication can continue)
- No failover triggered, service continues normally
Scenario C: All nodes partitioned from DCS
- Primary demotes, replica cannot elect
- Cluster completely unavailable
- Requires manual intervention to restore DCS connectivity
Comparison with Other Failures
| Failure Type | Primary State | Lease Handling | RTO | Split-brain Risk |
|---|---|---|---|---|
| Expire Failure | Node crash | Passive wait TTL expiration | 16s ~ 150s | None |
| Crash Failure | PG crash, Patroni alive | Release after restart timeout | 1s ~ 111s | None |
| Network Partition | Alive but isolated from DCS | Passive wait TTL expiration | 16s ~ 150s | Yes, needs protection |
| Manual Switchover | Normal or failed | Direct release/acquire | 1s ~ 11s | None |
A network partition has the same RTO as an expired-lease failure, but also needs split-brain protection. The constraint loop_wait + 2 × retry_timeout ≤ ttl is central to that protection.