Handling Galera Cluster Split-Brain and Recovery
The first time a Galera cluster went non-Primary on me, I thought the database was corrupted. It wasn't. Galera (MariaDB Galera, Percona XtraDB Cluster or the Codership build) only accepts writes on a node that is part of the Primary Component, the group holding more than half the votes. When a partition or a run of crashes leaves no group with a majority, every node stops serving. That is Galera avoiding split-brain, not suffering one, though the application sees the same thing: the database is down.
Commands here are for MariaDB 10.1/10.2 Galera on CentOS 7; other builds differ mostly in service names. I assume a three-node cluster (any odd size; two nodes need an arbitrator, garbd) and SSH access to every node plus the MySQL root credentials.
Read the cluster state on each node
SHOW STATUS WHERE Variable_name IN
('wsrep_cluster_status','wsrep_cluster_size','wsrep_local_state_comment',
'wsrep_ready','wsrep_connected','wsrep_last_committed','wsrep_cluster_state_uuid');
wsrep_cluster_status:Primarymeans the node can serve;non-Primarymeans it lost quorum;Disconnectedmeans it is not talking to anybody.wsrep_cluster_size: how many nodes this node sees.wsrep_local_state_comment:Syncedis normal;Donor/Desynced,JoinerandInitializedare transitional or broken.wsrep_last_committed: the sequence number of the last applied transaction, used to decide which node is most advanced.
An application trying to use a non-Primary node gets:
ERROR 1047 (08S01): WSREP has not yet prepared node for application use
A network partition
If the partition heals on its own, Galera re-merges automatically and the minority side re-syncs via IST (incremental state transfer). Nothing to do except watch wsrep_cluster_size return to 3.
If the partition is permanent, for example one data center is gone, and the surviving nodes are a minority (one out of three), you have to tell them to become Primary manually. Do this on exactly one node and only once you are certain the other side is not still serving writes:
SET GLOBAL wsrep_provider_options = 'pc.bootstrap=true';
The node forms a new Primary Component; other reachable nodes rejoin it. When the lost nodes come back, they will see a different cluster history and perform SST (full state snapshot) from the survivors. If they had accepted writes in the meantime, those writes are discarded, which is the actual split-brain you want to avoid. Verify twice before bootstrapping; I check from a second terminal that the other side really is down.
After a full shutdown or crash
After a power failure, all nodes are down and none has a Primary Component to join. Galera refuses to start any node normally; the error log shows:
[ERROR] WSREP: It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates.
First find the most advanced node. Check grastate.dat on each:
cat /var/lib/mysql/grastate.dat
# GALERA saved state
version: 2.1
uuid: 5c6f2e3a-4b1d-11e9-8f0c-0800271e1a2b
seqno: 184271
safe_to_bootstrap: 1
On a clean shutdown, the last node to stop has safe_to_bootstrap: 1 and a real seqno. After a crash, seqno is -1 on every node; recover it with:
sudo -u mysql mysqld --wsrep-recover
or, on MariaDB, galera_recovery. The log prints WSREP: Recovered position: <uuid>:<seqno>. Pick the node with the highest seqno, set safe_to_bootstrap: 1 in its grastate.dat, and bootstrap it:
# MariaDB 10.1+
sudo galera_new_cluster
# Percona XtraDB Cluster 5.7
sudo systemctl start mysql@bootstrap.service
Then start the other nodes normally with systemctl start mariadb. They join with IST if their gcache still covers the gap, or with SST otherwise.
Making the next one less painful
- Use three or five nodes, or two nodes plus
garbdon a third machine. - Set
pc.weightinwsrep_provider_optionsso a primary site keeps quorum if a secondary site disappears (for example weight 2 on one node and 1 on the others). - Size
gcache.size(default 128M) so a node that is down for the length of a typical maintenance can rejoin with IST instead of a full SST. - Use
xtrabackup-v2ormariabackupaswsrep_sst_method, notrsync, so a donor stays available during SST.
[galera]
wsrep_provider_options = "gcache.size=2G; pc.weight=1; gmcast.segment=0"
wsrep_sst_method = mariabackup
wsrep_sst_auth = sstuser:Sst-P@ss
Did it work?
On every node after recovery:
SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_local_state_comment';
All three should report the same wsrep_cluster_state_uuid, size 3, Primary and Synced. Do a write on one node and read it on another to confirm replication flows in both directions.
Log lines I have seen
[ERROR] WSREP: failed to open gcomm backend connection: 110: failed to reach primary view: 110 (Connection timed out)on a joining node: it cannot reach any address inwsrep_cluster_address, usually a firewall. Galera needs TCP 4567 (group communication), 4568 (IST) and 4444 (SST) between nodes.[ERROR] WSREP: SST failed: 1 (Operation not permitted)orWSREP_SST: [ERROR] xtrabackup_checkpoints missing: the SST user or password inwsrep_sst_authis wrong on the donor, ormariabackup/xtrabackupis not installed on both nodes. Check/var/lib/mysql/innobackup.backup.logon the donor.- A node stays in
Joinerfor a long time and the donor showsDonor/Desynced: SST is in progress. Withrsyncthe donor is read-only; withmariabackupit keeps serving. Do not restart either node. wsrep_cluster_sizeis 3 butwsrep_readyisOFFon one node: the node is a member but is not synced yet. Wait forwsrep_local_state_commentto reachSynced.
Galera would rather stop than diverge. Find the node with the newest data, bootstrap only that one, and let the rest catch up. Bootstrapping two is how real split-brain happens.