feat: MariaDB primary and replica on the mesh, semi-synchronous, the primary the VIP's holder (0031, 0049) #93
No reviewers
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Keel-Linux/keel!93
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/mariadb-pair-semisync"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 2, step 3: MariaDB primary and replica over the mesh, semi-synchronous, the primary the VIP's holder (decisions 0013, 0020, 0028, 0031, 0049, 0050; tracker#57; the maintainer's answers of 2026-10-03 and 2026-10-11). The design is
docs/replication.md; the four questions it asked were approved as recommended on 2026-10-11 (the precision to 0049 is Keel-Linux/handbook#57). keel 0.23.0.What it does
appliance.vipplusdatabase.server.role: the primary is the VIP's holder;rolein the spec is the installation's, decides who seeds from whom before the first claim, and is runtime state from then on (0020).inspectreports the VIP's role;diffshows the difference as information, never drift;applynever converges it. With the pair record,replication.primary,allowed_fromandsecretare derived and optional. Both nodes carry one drop-in (binary log, ROW,log_slave_updates,gtid_strict_mode, semi-sync both sides, TLS, the VIP bound beside the node's addresses throughnet.ipv6.ip_nonlocal_bind), so no role change restarts MariaDB.AFTER_SYNC,rpl_semi_sync_master_timeout=10000,rpl_semi_sync_master_wait_no_slave=OFF,slave_net_timeout=10; MariaDB waits for one replica's acknowledgement by design (wait_for_slave_countis MySQL's and 11.8 refuses to start with it: the first thing the end to end test caught).wait_no_slave=OFFis the third thing it caught: with both sides on and MariaDB's default, the replica's own SQL thread is a master with no slave and waited the 10 s on the first transaction it applied (traced: "Waiting for semi-sync ACK from slave" for 9.9 s); a node with no replica connected now does not wait, and a connected replica that does not answer still costs the primary the 10 s (the fallback). The fallback isdatabase.server.semi_sync: driftinkeel diffon the primary, andkeel database watch(keel-database-watch.timer, 30 s) alerts once on the fallback and once on the recovery through the monitor's channels. A write in flight when a primary is cut off commits after the 10 s and is errant: such a primary comes back diverged (documented, tested in (d)'s second run).read_onlyat run time, and both nodes boot read only (the role's drop-in):followlifts it on the holder at every boot, so a crashed old primary takes no write before it learns the newer epoch.READ_ONLY ADMINrevoked from every account but'mysql'@'localhost'andmariadb.sys, root included (0049); keel's own statements go throughrunuser -u mysql -- mariadb, and a node's account and grant statements run withsql_log_binoff (a grant logged under the replica's server id would stop it undergtid_strict_mode, ER 1950). docs/apply.md's "root still writes" is corrected.databaseleaf per node from the mesh root CA (CN<member> mariadb, no etcd user; SANs the node's /128 and the VIP; serverAuth+clientAuth), the root made bykeel mesh createin every cloud mode, asked of the holder or of the pair peer over the members' channel (issue,kind: database; the holder signs only for the address its spec gives the asker's key, and puts the VIP in the SANs only on the pair's signed record the request carries); the replication accountREQUIRE SUBJECT '/CN=<peer> mariadb' AND ISSUER '/CN=<root>'(an etcd leaf of the same root is refused, measured: 1045); the server checks the root's CRL (ssl_crl), refreshed by watch withFLUSH SSL;MASTER_SSL_VERIFY_SERVER_CERT=1; renewed by watch. Files under/etc/mysql/keel-tls./var/lib/keel/database/, asked by the other member overPOST /v1/vipkindsecret(answered only to the other member of the pair record): the first shared secret of 0041.keel database follow(the role hook,keel-database-follow.pathon/var/lib/keel/vipand at boot): holder → drained and promoted,read_onlyoff, the privilege back; other member →read_onlyon, the lock, a fresh server seeded over TLS, a replica of the holder left alone, an old primary compared by GTID (@@gtid_binlog_stateagainst the holder's state andgtid_slave_pos): none errant → dump kept,gtid_slave_pos= itsgtid_binlog_pos, follows; some → stays read only,database.server.divergedin diff andkeel database status, one alert;--destroy-local-databasereseeds.keel database promotemoves the VIP, then follows.keel database status; the role file/var/lib/keel/rolefor tracker#57.Seams (for the end to end test)
Three, all the clients' or keel's own conventions, never switches of the code:
KEEL_LIVE_ROOTnames the tree that stands for a node's live system (keel.inspect.constants.ROOT_DEFAULT; never set on a machine);MYSQL_HOMEnames an options file with that node's socket, read by the MariaDB clients after/etc/mysql/my.cnf;KEEL_MARIADB_SERVICEnames the node's unit (keel.system.dbmariadb.service()). The agents stand forkeel-database-follow.path(follow on a change of the VIP's state) andkeel-database-watch.timer(watch every 10 s there, 30 on a machine). The alert channel is a webhook the routing namespace serves.Tests
Pure, 3300+ passing, coverage 97 % (gate 95):
tests/test_system_dbpair.py(GTID comparison, the pair, the shared credential over the in-process channel and its refusals, the leaf issued by the holder and refused for another address or name, the pair plan, inspect's and diff's lines),tests/test_system_dbfollow.py(every branch of the follow table against a recorded MariaDB, watch's alerts once each way, status),tests/test_database_cli.py.End to end,
tests/test_mariadb_netns.py(CI jobmariadb / trixie):tests/vip_netns.py's three nodes on 250 ms ±25 ms with 2 % loss measured first, etcd, the VIP's units, a MariaDB 11.8 server per node as a systemd unit in its namespace reading the node's root's drop-in directory. This PR tests case (a): A and B applied, B seeded over TLS (Master_SSL_Allowed: Yes,Using_Gtid: Slave_Pos), three writes at the VIP each read on B with the lag measured from the write's acknowledgement, semi-sync on and a commit's cost, the replica read only with'mysql'@'localhost'alone holdingREAD_ONLY ADMIN,keel diffclean on both; then (the security review's three) B applied again and written through with its SQL thread still applying, B's etcd member leaf refused asreplwhere its database leaf is taken, and A restarted read only from its first second untilfollowlifts it.Security review (2026-10-08), what changed: HIGH, the authorizations outside the binary log; MEDIUM, the VIP SAN bound to the signed pair record,
REQUIRE SUBJECT ... AND ISSUERwith the root's CRL, both nodes boot read only; LOW, the fence recorded before the address is dropped, the fallback alerted at its first sight and no recovery alert at the first watch, the bounds tightened to the measured figures. Not this PR: the rotation of the replication credential (an issue to open).Deferred to the follow-up PR (branch
feat/mariadb-pair-scenario, with the rest of the harness: a writer at the VIP, the partitions and heals, the alerts' webhook): (b) the fallback after 10 s with the replica cut, drift and the alert, writes going on, the recovery; (c)keel database promotewhile the application writes, the downtime, no acknowledged row lost; (d) the primary cut off idle, failover per keel#81, the database following, the old primary rejoining by itself; then cut off while written to, coming back diverged, read only, alerted, reseeded once confirmed; (e) the replica refusingINSERTas root and as the application. The harness for three servers on one runner took ten local iterations (all plumbing: a private/tmpfor units in a netns, the servers' temporary directory, the manifests under the roots, the status parse), and found two product bugs on the way, both fixed here: MariaDB 11.8 has norpl_semi_sync_master_wait_for_slave_count, and the certificate files were unreachable by the server asmysql(now/etc/mysql/keel-tls, 0644/0640).Measured
Design case on every leg (measured over the overlay in CI: 226.5 ms minimum, 251 ms median, 3.75 % loss; locally 226.6 / 251 / 4 %).
mariadb / trixie)repl/ B's database leaffollowkeel diff, database section, both nodessemi_sync: same (on)on the primarysemi_sync: same (on)INSERTas root by the socket / as the application at the replica'mysql'@'localhost'holdsREAD_ONLY ADMIN@ -0,0 +187,4 @@gid = grp.getgrnam(GROUP).gr_gidexcept KeyError:returnos.chmod(directory, DIR_MODE)semgrep /
These permissions
DIR_MODEare widely permissive and grant access to more people than may be necessary. A good default is0o644which gives read and write access to yourself and read access to everyone else.Show more details