Summary
Setting CoMasterRecoveryMustPromoteOtherCoMaster: false is completely ineffective when the surviving co-master has read_only=OFF. Recovery always fails with:
RecoverDeadCoMaster: could not manage to promote other-co-master X; was only able to promote Y;
mustPromoteOtherCoMaster is true (either CoMasterRecoveryMustPromoteOtherCoMaster is true, or co-master
is writeable), therefore failing
Root Cause
In go/logic/topology_recovery.go, RecoverDeadCoMaster unconditionally overrides the config:
mustPromoteOtherCoMaster := config.Config.CoMasterRecoveryMustPromoteOtherCoMaster // false
if !otherCoMaster.ReadOnly {
AuditTopologyRecovery(...)
mustPromoteOtherCoMaster = true // unconditionally overrides the config setting!
}
Use Case: Active-Active M-M across two data centers
Site A (DC1): master-a (writable) ↔ master-b (writable) :Site B (DC2)
| |
replica-a (read-only) replica-b (read-only)
Config:
{
"CoMasterRecoveryMustPromoteOtherCoMaster": false,
"PreventCrossDataCenterMasterFailover": true
}
When master-a fails:
- We want replica-a promoted (same DC).
CoMasterRecoveryMustPromoteOtherCoMaster: false should allow this.
- BUT: master-b is writable →
mustPromoteOtherCoMaster is forced to true.
PreventCrossDataCenterMasterFailover: true blocks cross-DC promotion.
- Result: recovery always fails — a deadlock between the ReadOnly override and the cross-DC prevention.
Both masters must stay writable in active-active setups; setting read_only=ON on the co-master is not viable.
Fix
Respect the config as authority. Only set mustPromoteOtherCoMaster = true from the ReadOnly check when the config doesn't already say false:
mustPromoteOtherCoMaster := config.Config.CoMasterRecoveryMustPromoteOtherCoMaster
if !otherCoMaster.ReadOnly {
if mustPromoteOtherCoMaster {
AuditTopologyRecovery(topologyRecovery, fmt.Sprintf("RecoverDeadCoMaster: other co-master %+v is writeable hence has to be promoted", otherCoMaster.Key))
} else {
// CoMasterRecoveryMustPromoteOtherCoMaster=false: admin explicitly opted out.
// Respect the config rather than forcing promotion.
AuditTopologyRecovery(topologyRecovery, fmt.Sprintf("RecoverDeadCoMaster: other co-master %+v is writeable; CoMasterRecoveryMustPromoteOtherCoMaster=false overrides writeable check, will not force promotion", otherCoMaster.Key))
}
}
When CoMasterRecoveryMustPromoteOtherCoMaster=true (the default): behavior is identical to before — no regression.
Reproduction
- M-M replication: master-a ↔ master-b, replica-a replicates from master-a.
- Both masters
read_only=OFF (active-active).
- Config:
CoMasterRecoveryMustPromoteOtherCoMaster: false, PreventCrossDataCenterMasterFailover: true.
- Kill master-a.
- Observe: recovery fails with
mustPromoteOtherCoMaster is true ... therefore failing.
Versions
Confirmed in v3.2.6 (openark) and v3.2.6-22 (percona fork).
Summary
Setting
CoMasterRecoveryMustPromoteOtherCoMaster: falseis completely ineffective when the surviving co-master hasread_only=OFF. Recovery always fails with:RecoverDeadCoMaster: could not manage to promote other-co-master X; was only able to promote Y;
mustPromoteOtherCoMaster is true (either CoMasterRecoveryMustPromoteOtherCoMaster is true, or co-master
is writeable), therefore failing
Root Cause
In
go/logic/topology_recovery.go,RecoverDeadCoMasterunconditionally overrides the config:Use Case: Active-Active M-M across two data centers
Config:
When master-a fails:
CoMasterRecoveryMustPromoteOtherCoMaster: falseshould allow this.mustPromoteOtherCoMasteris forced to true.PreventCrossDataCenterMasterFailover: trueblocks cross-DC promotion.Both masters must stay writable in active-active setups; setting read_only=ON on the co-master is not viable.
Fix
Respect the config as authority. Only set mustPromoteOtherCoMaster = true from the ReadOnly check when the config doesn't already say false:
When
CoMasterRecoveryMustPromoteOtherCoMaster=true(the default): behavior is identical to before — no regression.Reproduction
read_only=OFF(active-active).CoMasterRecoveryMustPromoteOtherCoMaster: false,PreventCrossDataCenterMasterFailover: true.mustPromoteOtherCoMaster is true ... therefore failing.Versions
Confirmed in v3.2.6 (openark) and v3.2.6-22 (percona fork).