Commit 53a1a9d
authored
Monitor: detect and recover from fast_forward candidate stuck with all WAL sources unhealthy (#1143)
* Fix fast_forward self-reference infinite loop
When a node is assigned the fast_forward goalstate it has not yet
reported that state back to the monitor, so reportedstate is still
report_lsn. If the originally-selected upstream peer transitions
out of report_lsn before the fast_forward node calls
get_most_advanced_standby(), the fast_forward node becomes the only
remaining report_lsn node and is returned as its own upstream source.
The node then loops forever trying to fetch WAL from itself.
Fix across three layers:
SQL (get_most_advanced_standby): add caller_node_id bigint parameter
(default 0) and filter AND nodeid != $3 so the calling node can
never be returned as its own upstream.
monitor_get_most_advanced_standby: accept callerNodeId and a bool
*found output parameter. A zero-row result is no longer an error:
it means the caller is already the most advanced node and the found
flag is set to false.
keeper_get_most_advanced_standby: pass the local node ID to the
monitor call; also skip self in the no-monitor path. Propagates the
found flag to the caller.
fsm_fast_forward: when found is false (no valid upstream), skip the
WAL fetch and return true so the keeper can report its current state
to the monitor. The monitor will then assign prepare_promotion on
the next node_active call, breaking the loop.
fsm_init_from_standby also uses keeper_get_most_advanced_standby;
there a not-found result is a genuine error (no source to clone from)
so it returns false with an explicit log message.
Fixes #1060
* Monitor: detect fast_forward candidate stuck with all WAL sources unhealthy
When a failover candidate is assigned fast_forward (its LSN lags behind a
peer standby that holds more WAL), it fetches the missing WAL before
promotion. If the WAL-source node(s) die while the candidate is fetching,
the candidate gets stuck: it reports back report_lsn (failed fetch) while
its goal stays fast_forward. IsBeingPromoted() holds the lock, so the
election cannot restart, and ProceedWithMSFailover does nothing useful
because CandidateNodeIsReadyToStreamWAL explicitly excludes fast_forward.
Fix (group_state_machine.c):
Add WalSourceNodesAreAllUnhealthy() helper that scans the group for
nodes in {report_lsn, report_lsn} (the WAL-source state) whose health
is bad. When this fires for a candidate in {report_lsn, fast_forward}
inside ProceedGroupStateForMSFailover, the monitor acts based on the
guard_data_loss GUC:
- guard_data_loss=true (default): reset the candidate goal back to
report_lsn and WARN the operator. The election retries automatically
if a source recovers. Operators can unblock with:
pg_autoctl perform failover --allow-data-loss
- guard_data_loss=false: log and fall through.
get_most_advanced_standby() now filters out unhealthy nodes when
guard_data_loss=false (SQL change below), so fsm_fast_forward() finds
no upstream, skips the WAL fetch, and reports fast_forward as current.
The monitor then assigns prepare_promotion on the next call.
Fix (pgautofailover.sql):
get_most_advanced_standby() adds:
AND (current_setting('pgautofailover.guard_data_loss')::bool
OR health > 0)
When guard_data_loss=false, unhealthy nodes are excluded from WAL-source
selection. When guard_data_loss=true, the highest-LSN node is returned
regardless of health (preserving the no-data-loss guarantee).
Tests:
src/monitor/sql/fast_forward.sql - regression test: bootstraps a 3-node
formation, manually places the candidate in {report_lsn, fast_forward}
with the WAL source unhealthy, then:
Test A (guard_data_loss=true): node_active returns report_lsn ✓
Test B (guard_data_loss=false): node_active returns fast_forward ✓
Also calls get_most_advanced_standby() directly to exercise both SQL
filter paths.
tests/tap/specs/fast_forward.pgaf - integration test: kills primary and
WAL-source standby together, verifies node stays at report_lsn, then
unblocks with --allow-data-loss and verifies full cluster recovery.
src/monitor/Makefile and tests/tap/schedule updated accordingly.
Fixes #10601 parent ca7834b commit 53a1a9d
12 files changed
Lines changed: 961 additions & 21 deletions
File tree
- src
- bin/pg_autoctl
- monitor
- expected
- sql
- tests/tap
- specs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1320 | 1320 | | |
1321 | 1321 | | |
1322 | 1322 | | |
| 1323 | + | |
1323 | 1324 | | |
1324 | 1325 | | |
1325 | 1326 | | |
1326 | | - | |
1327 | | - | |
| 1327 | + | |
| 1328 | + | |
1328 | 1329 | | |
1329 | 1330 | | |
1330 | 1331 | | |
1331 | 1332 | | |
1332 | 1333 | | |
1333 | 1334 | | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
1334 | 1348 | | |
1335 | 1349 | | |
1336 | 1350 | | |
| |||
1471 | 1485 | | |
1472 | 1486 | | |
1473 | 1487 | | |
| 1488 | + | |
1474 | 1489 | | |
1475 | 1490 | | |
1476 | | - | |
| 1491 | + | |
1477 | 1492 | | |
1478 | 1493 | | |
1479 | 1494 | | |
1480 | 1495 | | |
1481 | 1496 | | |
1482 | 1497 | | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
1483 | 1505 | | |
1484 | 1506 | | |
1485 | 1507 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3109 | 3109 | | |
3110 | 3110 | | |
3111 | 3111 | | |
3112 | | - | |
| 3112 | + | |
| 3113 | + | |
3113 | 3114 | | |
3114 | 3115 | | |
3115 | 3116 | | |
| 3117 | + | |
3116 | 3118 | | |
3117 | 3119 | | |
3118 | 3120 | | |
| |||
3121 | 3123 | | |
3122 | 3124 | | |
3123 | 3125 | | |
3124 | | - | |
| 3126 | + | |
| 3127 | + | |
| 3128 | + | |
3125 | 3129 | | |
3126 | 3130 | | |
3127 | 3131 | | |
| |||
3140 | 3144 | | |
3141 | 3145 | | |
3142 | 3146 | | |
| 3147 | + | |
| 3148 | + | |
| 3149 | + | |
| 3150 | + | |
| 3151 | + | |
| 3152 | + | |
3143 | 3153 | | |
3144 | 3154 | | |
3145 | 3155 | | |
| |||
3158 | 3168 | | |
3159 | 3169 | | |
3160 | 3170 | | |
3161 | | - | |
3162 | | - | |
3163 | | - | |
3164 | | - | |
3165 | | - | |
| 3171 | + | |
| 3172 | + | |
| 3173 | + | |
| 3174 | + | |
3166 | 3175 | | |
3167 | 3176 | | |
3168 | 3177 | | |
| 3178 | + | |
3169 | 3179 | | |
3170 | 3180 | | |
3171 | 3181 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
123 | | - | |
| 123 | + | |
| 124 | + | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
751 | 751 | | |
752 | 752 | | |
753 | 753 | | |
754 | | - | |
| 754 | + | |
| 755 | + | |
755 | 756 | | |
756 | 757 | | |
757 | 758 | | |
758 | | - | |
759 | | - | |
760 | | - | |
761 | | - | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
762 | 763 | | |
763 | | - | |
| 764 | + | |
764 | 765 | | |
765 | 766 | | |
766 | 767 | | |
767 | 768 | | |
| 769 | + | |
768 | 770 | | |
769 | 771 | | |
770 | 772 | | |
| 773 | + | |
771 | 774 | | |
772 | 775 | | |
773 | 776 | | |
| |||
781 | 784 | | |
782 | 785 | | |
783 | 786 | | |
784 | | - | |
| 787 | + | |
785 | 788 | | |
786 | 789 | | |
787 | 790 | | |
| |||
792 | 795 | | |
793 | 796 | | |
794 | 797 | | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
795 | 807 | | |
796 | 808 | | |
797 | 809 | | |
| |||
803 | 815 | | |
804 | 816 | | |
805 | 817 | | |
| 818 | + | |
806 | 819 | | |
807 | 820 | | |
808 | 821 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
| 127 | + | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
0 commit comments