fix: a PostgreSQL standby caught up after the reseed may take the VIP again (keel#166) #172

Merged
navigator merged 1 commit from fix/pg-reseed-clears-vip-fence-166 into main 2026-10-15 12:01:47 -03:00
Owner

Fixes #166. Stacked on #171: merge #170 and #171 first.

After an automatic failover, the old holder is fenced in its VIP state, and a fenced node never claims by itself (0049). The confirmed reseed did not clear that fence. On real nodes the second automatic failover did not come.

Change

  • keel database follow clears the fence of a PostgreSQL standby that streams from the holder and received all that the holder flushed. The holder's flushed position comes first, from IDENTIFY_SYSTEM, then the standby's received position. "Caught up", not only "streaming": a commit that the holder acknowledged alone (#165) is then on this node too.
  • The follow does this after a seed or a reseed, and on each run that finds the node a standby of the holder.
  • keel database watch runs follow on a fenced PostgreSQL standby that streams. So a node that is fenced now (9062 on BR2) is cleared at its first watch run after the upgrade.
  • A node fenced on its own claim stays fenced. MariaDB is not changed.

Tests

RED first: 4 tests in tests/test_system_dbpgfollow.py (caught up, behind, holder silent, reseed) and 1 in tests/test_system_dbfollow.py (the watch). netns: the reseeded old primary is not fenced within 3 watch runs.

Fixes #166. Stacked on #171: merge #170 and #171 first. After an automatic failover, the old holder is fenced in its VIP state, and a fenced node never claims by itself (0049). The confirmed reseed did not clear that fence. On real nodes the second automatic failover did not come. ## Change - `keel database follow` clears the fence of a PostgreSQL standby that streams from the holder and received all that the holder flushed. The holder's flushed position comes first, from `IDENTIFY_SYSTEM`, then the standby's received position. "Caught up", not only "streaming": a commit that the holder acknowledged alone (#165) is then on this node too. - The follow does this after a seed or a reseed, and on each run that finds the node a standby of the holder. - `keel database watch` runs follow on a fenced PostgreSQL standby that streams. So a node that is fenced now (9062 on BR2) is cleared at its first watch run after the upgrade. - A node fenced on its own claim stays fenced. MariaDB is not changed. ## Tests RED first: 4 tests in `tests/test_system_dbpgfollow.py` (caught up, behind, holder silent, reseed) and 1 in `tests/test_system_dbfollow.py` (the watch). netns: the reseeded old primary is not fenced within 3 watch runs.
feat: with etcd, an expired lease states the old PostgreSQL primary gone (keel#165)
All checks were successful
security / security (pull_request) Successful in 1m56s
tests / etcd (pull_request) Successful in 7m27s
tests / etcd-auth (pull_request) Successful in 5m5s
tests / vip (pull_request) Successful in 6m34s
tests / vip-upgrade (pull_request) Successful in 7m25s
tests / package (pull_request) Successful in 5s
tests / mesh (pull_request) Successful in 11m35s
tests / mariadb-simple (pull_request) Successful in 4m39s
tests / mariadb (pull_request) Successful in 6m24s
tests / postgres (pull_request) Successful in 10m7s
tests / tests (pull_request) Successful in 28m13s
312e2db482
The maintainer decided keel#165 on 2026-10-15 (handbook 0054, amendment
of 2026-10-15). With etcd, an automatic failover of the PostgreSQL pair
moved the VIP, but the new primary took no write until the operator ran
keel database promote --old-primary-gone.

- The VIP controller claims after the lease of the newest claim is gone.
  When that lease expired at its due time, and the controller saw it
  alive before, the claim states that the old primary is gone. The root
  half writes /var/lib/keel/database/old-primary-gone, with the epoch of
  the claim, before it records the claim. So the follow that the claim
  starts promotes the standby and commits alone.
- A revoked lease, or a lease that the controller never saw alive (after
  a restart of the controller), states nothing. The holder that claims
  again after a revoke states nothing.
- keel database follow takes a statement with no epoch (the operator's)
  or one for the epoch of this node's newest claim. It removes a
  statement of another epoch unused.
- The root half cannot ask etcd again. It trusts the word of its own
  helper for this statement only, and it checks the claim: verified,
  this node's own, above the epoch it held, the previous holder another
  node.
- The netns case (c) now runs with no operator promote: B's controller
  claims when A's lease expired, and B commits alone.
fix: keel database promote makes the database writable before it joins the late asks (keel#169)
Some checks failed
security / security (pull_request) Successful in 1m1s
tests / etcd (pull_request) Successful in 6m57s
tests / vip (pull_request) Successful in 7m57s
tests / mesh (pull_request) Successful in 11m11s
tests / etcd-auth (pull_request) Successful in 4m43s
tests / vip-upgrade (pull_request) Successful in 7m46s
tests / package (pull_request) Successful in 9s
tests / mariadb-simple (pull_request) Successful in 4m52s
tests / mariadb (pull_request) Successful in 6m33s
tests / postgres (pull_request) Failing after 9m1s
tests / tests (pull_request) Has been cancelled
b4a593d1bd
On a real pair with etcd, keel database promote --old-primary-gone made
the database writable 11.9 s and 12.9 s after the VIP moved to it. The
bound of keel#148 is 5 s.

keel vip promote bounds each ask of the gone old primary to 1.5 s, but
it joins the late asks before it returns. A dead node costs the members'
channel's 10 s connect timeout, and keel database promote observed and
followed only after that join. CI did not see it: the harness follows
the VIP's state on its own, as keel-database-follow.path does, and that
path had failed on the real nodes (keel#164).

- vippromote.promote takes `then`, a step that runs once the promote
  succeeded, before the join. keel database promote passes its database
  step there. The promote still joins the asks before it exits.
- A refused promote runs no database step. The operator's statement is
  removed only when the database step did not run.
fix: a PostgreSQL standby caught up after the reseed may take the VIP again (keel#166)
Some checks failed
security / security (pull_request) Successful in 1m40s
tests / etcd (pull_request) Successful in 6m50s
tests / vip (pull_request) Successful in 6m46s
tests / tests (pull_request) Has been cancelled
tests / package (pull_request) Has been cancelled
tests / mariadb-simple (pull_request) Has been cancelled
tests / postgres (pull_request) Has been cancelled
tests / mariadb (pull_request) Has been cancelled
tests / mesh (pull_request) Has been cancelled
tests / vip-upgrade (pull_request) Has been cancelled
tests / etcd-auth (pull_request) Has been cancelled
10ea05501f
After an automatic failover, the old holder is fenced in its VIP state.
A fenced node never claims by itself (0049). The confirmed reseed did
not clear that fence, so on real nodes the second automatic failover
did not come: the VIP stayed on no node until the operator ran keel
database promote --old-primary-gone.

- keel database follow clears the fence of a PostgreSQL standby that
  streams from the holder and received all that the holder flushed.
  The holder's flushed position comes first, from IDENTIFY_SYSTEM, then
  the standby's received position. Caught up, not only streaming: a
  commit that the holder acknowledged alone is then on this node too.
- The follow does this after a seed or a reseed, and on each run that
  finds the node a standby of the holder.
- keel database watch runs follow on a fenced PostgreSQL standby that
  streams. A node that is fenced now is cleared at its next watch run.
- A node fenced on its own claim stays fenced.
- The netns test checks that the reseeded old primary is not fenced
  within 3 watch runs.
navigator force-pushed fix/pg-reseed-clears-vip-fence-166 from 10ea05501f
Some checks failed
security / security (pull_request) Successful in 1m40s
tests / etcd (pull_request) Successful in 6m50s
tests / vip (pull_request) Successful in 6m46s
tests / tests (pull_request) Has been cancelled
tests / package (pull_request) Has been cancelled
tests / mariadb-simple (pull_request) Has been cancelled
tests / postgres (pull_request) Has been cancelled
tests / mariadb (pull_request) Has been cancelled
tests / mesh (pull_request) Has been cancelled
tests / vip-upgrade (pull_request) Has been cancelled
tests / etcd-auth (pull_request) Has been cancelled
to 220f9238e5
Some checks failed
security / security (pull_request) Successful in 1m27s
tests / etcd (pull_request) Successful in 6m55s
tests / vip (pull_request) Successful in 7m25s
tests / mesh (pull_request) Failing after 11m27s
tests / etcd-auth (pull_request) Successful in 5m12s
tests / package (pull_request) Successful in 8s
tests / mariadb (pull_request) Successful in 5m57s
tests / mariadb-simple (pull_request) Successful in 4m37s
tests / vip-upgrade (pull_request) Successful in 7m23s
tests / tests (pull_request) Has been cancelled
tests / postgres (pull_request) Has been cancelled
2026-10-15 10:30:11 -03:00
Compare
navigator force-pushed fix/pg-reseed-clears-vip-fence-166 from 220f9238e5
Some checks failed
security / security (pull_request) Successful in 1m27s
tests / etcd (pull_request) Successful in 6m55s
tests / vip (pull_request) Successful in 7m25s
tests / mesh (pull_request) Failing after 11m27s
tests / etcd-auth (pull_request) Successful in 5m12s
tests / package (pull_request) Successful in 8s
tests / mariadb (pull_request) Successful in 5m57s
tests / mariadb-simple (pull_request) Successful in 4m37s
tests / vip-upgrade (pull_request) Successful in 7m23s
tests / tests (pull_request) Has been cancelled
tests / postgres (pull_request) Has been cancelled
to 85a6d3ceaf
All checks were successful
security / security (pull_request) Successful in 1m35s
tests / etcd (pull_request) Successful in 7m9s
tests / vip (pull_request) Successful in 7m41s
tests / mesh (pull_request) Successful in 11m7s
tests / etcd-auth (pull_request) Successful in 4m55s
tests / package (pull_request) Successful in 8s
tests / vip-upgrade (pull_request) Successful in 7m45s
tests / mariadb-simple (pull_request) Successful in 4m56s
tests / mariadb (pull_request) Successful in 5m53s
tests / postgres (pull_request) Successful in 9m44s
tests / tests (pull_request) Successful in 28m2s
2026-10-15 11:17:06 -03:00
Compare
Sign in to join this conversation.
No description provided.