Preventing split-brain during CCR network partitions
This runbook keeps exactly one writer alive while a Cross-Cluster Replication (CCR) link is partitioned, then promotes the follower only after the old leader is provably fenced — the sequence that stops a partition from forking one index into two divergent histories. It is the step-by-step operator procedure behind the model in Split-Brain Prevention.
Prerequisites
Confirm each item before a partition happens — this runbook is executed under pressure, and a missing prerequisite is discovered at the worst possible moment.
Decision flow
The whole runbook is one decision: promote the follower only if the old leader is fenced, otherwise wait. The flow below is the shape of that decision — every step in the procedure maps to one node in it.
Step-by-step procedure
1. Detect the partition and freeze the promotion instinct
Before touching anything, confirm the partition is real and not a slow link. Check replication status from the follower side and the remote connection health.
GET _plugins/_replication/logs-000001/_status
GET _remote/info
# follower _status during a partition
"status" : "SYNCING",
"syncing_details" : { "leader_checkpoint" : 90418, "follower_checkpoint" : 90418 }
# _remote/info
"dc-primary" : { "connected" : false, "num_nodes_connected" : 0 }
Gotcha: a follower still reports
SYNCINGimmediately after a partition because it does not yet know the leader is gone —"connected": falsefrom_remote/infois the authoritative partition signal, not the replication status.
2. Fence the old leader before promoting anything
If the old leader is still reachable by application traffic, write-block it locally so it cannot accept another write. This is settable from the leader’s own side without the follower’s participation.
PUT logs-000001/_block/write
{ "acknowledged" : true, "shards_acknowledged" : true, "indices" : [ { "name" : "logs-000001", "blocked" : true } ] }
Gotcha: if the old leader is unreachable from your operator station too, fencing by write block is impossible — rely on the dropped-seeds step on the follower side and your arbiter’s decision, and never promote on the assumption that “unreachable to me” means “writing to no one”.
3. Confirm the follower is caught up within the lag budget
Promoting a lagging follower loses acknowledged writes. Check the checkpoint gap and only proceed if it is within your budget.
GET _plugins/_replication/logs-000001/_status
"syncing_details" : { "leader_checkpoint" : 90418, "follower_checkpoint" : 90418 }
# gap = 0 ops -> zero-loss promotion is possible
Gotcha: the last checkpoint the follower saw is the last one before the partition — a zero gap here means no acknowledged writes are lost, but writes the old leader accepted after the partition are gone unless the fence let you recover them. That is expected and correct: those post-partition writes are exactly what fencing discards to keep one history.
4. Stop replication and promote
With the leader fenced and the follower caught up, stop replication to make the former follower writable, then repoint the write alias.
POST _plugins/_replication/logs-000001/_stop
{
"leader_alias": "dc-primary",
"leader_index": "logs-000001"
}
POST _aliases
{
"actions": [
{ "add": { "index": "logs-000001", "alias": "logs-write", "is_write_index": true } }
]
}
Gotcha: the
_stopbody must repeat the exactleader_aliasandleader_indexthe replication was started with; a mismatch leaves the follower write-blocked and un-promotable, which under partition pressure looks identical to a hung cluster.
5. Reconcile the old leader as a follower after heal
When the link returns, the old leader rejoins as a follower against the new leader. Drop its stale role and start replication in the reverse direction.
POST _plugins/_replication/logs-000001/_start
{
"leader_alias": "dc-standby",
"leader_index": "logs-000001",
"use_roles": { "leader_cluster_role": "ccr_leader", "follower_cluster_role": "ccr_follower" }
}
Gotcha: if retention on the new leader still covers the old leader’s last checkpoint, this
_startresumes incrementally; if not, it silently falls back to a full remote bootstrap. Watch the status forBOOTSTRAPPINGversusSYNCINGto tell which path you got.
Verification
Confirm exactly one writer exists and replication now flows the new direction.
# Only the promoted cluster should own the write alias
GET logs-write/_alias
# promoted cluster: { "logs-000001": { "aliases": { "logs-write": { "is_write_index": true } } } }
# fenced cluster: write block present, alias NOT is_write_index
# Reverse replication is healthy
GET _plugins/_replication/logs-000001/_status
# "status": "SYNCING" with a shrinking checkpoint gap
A correct end state has the write alias resolving to one cluster only, the other cluster either write-blocked or replicating as a follower, and a bounded, shrinking checkpoint gap.
Common failures
| Symptom | Root cause | Fix command |
|---|---|---|
Both clusters own logs-write |
Follower promoted without fencing the old leader | PUT logs-000001/_block/write on the losing side; discard its post-partition writes |
_stop returns 400 |
leader_alias/leader_index in the body mismatch the original _start |
Re-issue _stop with values from the original replication _status |
Reverse _start triggers full bootstrap |
Retention on the new leader aged out the old leader’s checkpoint | Accept the bootstrap now; raise index.plugins.replication.translog.retention_size for next time |
| Follower promoted but writes rejected | Alias repoint missed is_write_index: true |
Re-run POST _aliases with is_write_index: true on the promoted index |
| Old leader keeps taking writes after heal | Its write block was never set during the partition | PUT logs-000001/_block/write then reconcile from the fenced side |
Frequently asked questions
What if I cannot reach either cluster to fence the old leader?
Then you are on the minority side of the partition and must not promote. Fencing requires that you can either set a write block on the old leader or provably cut it off (dropped seeds plus an arbiter decision). If you can do neither, promoting the follower risks a live old leader still taking writes — the exact split-brain condition. Wait for reachability or defer to the arbiter that names the authoritative side.
How current does the follower have to be before I promote it?
Within your lag budget, expressed as a checkpoint gap. A zero gap means no acknowledged writes are lost. Any writes the old leader accepted after the partition are discarded by design — fencing chooses one history, and the follower’s is the one that survives. If your tolerance for lost post-partition writes is zero, you cannot fail over during an active partition at all; you must wait for the link to heal.
Why repoint a write alias instead of just writing to the promoted cluster directly?
Because the alias is the single point that defines “the writer”. If applications write to named cluster endpoints, a partition leaves half of them still writing to the old leader — that is split-brain by configuration. Routing every write through logs-write means promotion is one atomic repoint and there is never a moment when two endpoints are both “the writer”.
Related
- Split-Brain Prevention — the split-brain model and fenced-failover automation this runbook executes.
- Tuning CCR checkpoint retention to avoid remote resync — sizing retention so step 5 resumes instead of bootstrapping.
- Follower Policy Design — keeping the follower write-blocked and terminal-action-safe.