Repository navigation
fix: an old primary stays read only until it proves it holds the VIP (keel#104) - #111
Merged
Merged
Conversation
4 tasks
marcos-mendez
force-pushed
the
fix/old-primary-read-only-until-proven
branch
from
October 9, 2026 01:03
3a2a0f0 to
617bfbb
Compare
…(keel#104) After a crash and a boot, keel database follow read the claim of the VIP in the node's state file and set read_only OFF. The other node held the VIP at a newer epoch. On a real pair the old primary was writable for 31 s, until keel vip check learned the newer epoch. Follow now lifts read_only only when this node's own claim is the newest it knows and the VIP is on wg0. keel adds the VIP to wg0 only after keel vip promote's new claim, keel vip check when a peer answered and no peer knows a newer claim, or with etcd a lease renewal that the majority confirmed. A boot removes the address. Without the proof, follow sets read_only ON and the root lock, and writes no primary role file. This is also true when etcd or the peers do not answer at boot, and on a paired node with no claim at all (its state file removed, or before the first promote). Follow waits up to 15 s for the VIP, because a promote records its claim before it adds the VIP. carry_held writes the VIP's state again when it adds the VIP, so keel-database-follow.path runs follow again. keel database watch follows a server again when it takes writes with no claim or while the VIP is not on wg0. One follow runs at a time, under /var/lib/keel/database/follow.lock. Without etcd the address has no lifetime, so the proof is weaker there; keel#113 records it, and docs/replication.md says so. Tests: unit tests for the crash, the newer epoch on the other node, the boot, read only, then the rejoin as a replica; etcd that does not answer; the state file removed on a primary; the VIP gone from wg0 on a writable primary; the lock. In mariadb / trixie, a case kills the primary, promotes the replica, boots the old primary and samples read_only; A now takes the VIP before the application's data.
marcos-mendez
force-pushed
the
fix/old-primary-read-only-until-proven
branch
from
October 9, 2026 01:26
617bfbb to
7cbfab1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #104 (bug B2 of the real-node MariaDB pair test, Keel-Linux/handbook#60).
Problem
After a crash and a boot, the old primary read its own claim of the VIP from
/var/lib/keel/vipandkeel database followsetread_only OFF. The other node held the VIP at a newer epoch. The old primary was writable for 31 s, untilkeel vip checklearned the newer epoch. A write in that window makes an errant GTID.Change
keel/system/dbfollow.py: on a node whose newest claim is its own, follow liftsread_onlyonly when the VIP is onwg0. keel adds the VIP towg0only after a proof:keel vip promoteafter its new claim;keel vip checkwhen a peer answered and no peer knows a newer claim; with etcd, the controller after a lease renewal that the majority confirmed (the kernelvalid_lftends it). A boot removes the address. Without the proof, follow setsread_only ONand the root lock, and writes no primary role file. Follow waits up to 15 s (PROOF_WAIT) for the address, because a promote records its claim before it adds the VIP.keel/mesh/vipnode.py(carry_held): when keel adds the VIP towg0, it writes the VIP's state again, sokeel-database-follow.pathruns follow again.keel database watchalso runs follow again within 30 s.keel/system/dbwatch.py(needs_follow): follows a server again when it takes writes with no claim, or while the VIP is not onwg0(itsvalid_lftran out).keel/system/dbfollow.py(follow): one run at a time, under/var/lib/keel/database/follow.lock, so the unit andkeel database watchnever interleave between the proof andSET read_only OFF.docs/replication.mdsays so.docs/replication.md: the follow table and the rule.debian/changelog,pyproject.toml,keel/__init__.py: 0.23.4.Tests
tests/test_system_dbfollow.py,TestTheOldPrimaryAtBoot: crash, the other node promoted at a newer epoch, the old node boots, stays read only, then follows as a replica (cloud simple, as on the real mesh); a server found writable without a proof is made read only; the holder proven again after its boot takes writes; no peer answers; the proof comes while follow waits; etcd does not answer at boot. The old code fails five of these withSET GLOBAL read_only = OFF.TestNoClaimAndNoAddress: the state file removed on a primary with a pair record, follow keeps it read only; watch follows a writable primary whose VIP leftwg0; follow takes its lock.tests/test_mesh_vipmove.py: the state is written again whenkeel vip checkadds the VIP, and not when it is there.tests/mariadb_netns.py: A now takes the VIP (and follow runs) before the application's data is written, because a paired node with no claim is read only.tests/mariadb_netns.py(mariadb / trixie): the primary's server killed, its VIP helper stopped, cut off;keel database promote --old-primary-goneon the replica; the old primary boots with its old claim:@@read_onlyis 1 at boot, after follow, and in every 0.5 s sample until it replicates from the new primary over TLS by GTID.Test plan
CI record
First run (617bfbb's first version):
mariadb / trixiefailed in the new case only: the replica's I/O thread was stillConnectingwhen the status was read (read only held in every sample, replica after 6.4 s). The case now waits for the I/O thread to stream, as case (a) does (amended).vip / trixiefailed atg_promote_b(etcdctl get: context deadline exceeded), the same failure as main's run 37833124848 and run 37843439840. No job was rerun: the amended push ran all checks again, all green.Security review: H1, M2 and L1 are fixed in the amended commit; M1 is #113.
Head 7cbfab1 (after the security review): every check green but
mesh / trixie. Attempt 1 timed out intest_a_spent_invite_is_refused(the inviter process). I reran the job once (run 37869774880, attempt 2); it failed in another test,test_c_joins_through_the_fallback, the same failure as main's run 37833124848. This PR changes nothing in keel.mesh's join or invite code. #112 holds this commit plus its own, and itsmesh / trixiepassed at 132a13a. I did not rerun a second time.