You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -238,7 +238,7 @@ return user.NeedsMigration() && migrate(user) || user
238
238
### EmergencyReparentShard (ERS)
239
239
- ERS must prioritize **certainty** that we picked the most-advanced candidate
240
240
- ERS must error when the most-advanced candidate is not clear, and/or a split-brain is suspected
241
-
- ERS must avoid introducing errant GTIDs on replicas. This includes writes that are considered unacknowledged to the client — at the errant-GTID level, whether or not the transaction was acknowledged to the client is inconsequential, as MySQL cannot rewind GTIDs of any kind
241
+
- ERS must avoid introducing errant GTIDs on replicas. This includes writes that are considered unacknowledged to the client as MySQL cannot rewind GTIDs of any kind
242
242
- Changes should prioritize reducing points of failure - avoid new RPCs or work that may delay or make ERS more brittle
243
243
- ERS must error if a shard contains a mix of GTID-based and non-GTID-based replication. Their position semantics differ (`Combined` = retrieved+executed for GTID vs. executed-only for non-GTID), so a unified split-brain / most-advanced check across both is unsafe
244
244
- For non-GTID flavors, ERS must wait on every candidate and fail on any error. The "filter to leading group + short-circuit on first success" optimization is only safe for GTID-based flavors, where `Combined` is distinct from the executed position
Copy file name to clipboardExpand all lines: changelog/25.0/25.0.0/summary.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -100,11 +100,14 @@ See [#19978](https://github.com/vitessio/vitess/issues/19978) for details.
100
100
101
101
`EmergencyReparentShard` (ERS) on GTID-based shards no longer fails when only some replicas can apply their relay logs. As long as at least one tablet at the leading `Combined` GTID position applies successfully, ERS proceeds; lagging or stuck-SQL-thread replicas are no longer blockers. Pre-existing pre-PR behavior is preserved for non-GTID flavors (FilePos, MariaDB), where ERS still requires every candidate to apply.
102
102
103
-
When the leading GTID-based candidates have incomparable `Combined` positions (suspected split-brain), ERS now aborts upfront with a clear `FAILED_PRECONDITION` error rather than risk silently picking one side.
103
+
When the leading GTID-based candidates have incomparable `Combined` positions (suspected split-brain), ERS now aborts upfront with a clear `FAILED_PRECONDITION` error naming the diverged tablets, rather than silently picking one side. Pre-PR ERS would pick blindly and let the losing side's unique GTIDs become errant on those tablets — a silent data-integrity incident that surfaced later via lag alerts or downstream consistency checks. See [#20199](https://github.com/vitessio/vitess/issues/20199) for the bug this addresses.
104
104
105
-
Two new stats are exported for observability:
105
+
A new `--allow-split-brain-promotion` flag is added to `vtctldclient EmergencyReparentShard` (and `--allow_split_brain_promotion` on the legacy `vtctl`). It is **off by default**. Operators who deliberately need to force ERS through a detected split-brain — typically because they already know which side to keep and plan to re-clone the losing side — can set it to convert the abort into a `WARN` log and proceed. The losing side's unique GTIDs will become errant after promotion, so this is an explicit operator override, not a default-on safety knob.
106
+
107
+
Three new stats are exported for observability:
106
108
107
109
-`EmergencyReparentFilteredCandidates` — counts replicas excluded from the relay-log wait because their `Combined` position is strictly behind the leading group.
108
110
-`EmergencyReparentRelayLogFailedCandidates` — counts replicas that genuinely failed to apply relay logs (cancellations after a peer succeeded are not counted).
111
+
-`EmergencyReparentSplitBrainOverrides` — counts ERS runs that proceeded despite detected split-brain because `--allow-split-brain-promotion` was set. Stays at zero unless an operator has deliberately invoked the escape hatch.
109
112
110
113
See [#18707](https://github.com/vitessio/vitess/pull/18707) for details.
EmergencyReparentShard.Flags().StringVar(&emergencyReparentShardOptions.ExpectedPrimaryAliasStr, "expected-primary", "", "Alias of a tablet that must be the current primary in order for the reparent to be processed.")
310
312
EmergencyReparentShard.Flags().BoolVar(&emergencyReparentShardOptions.PreventCrossCellPromotion, "prevent-cross-cell-promotion", false, "Only promotes a new primary from the same cell as the previous primary.")
311
313
EmergencyReparentShard.Flags().BoolVar(&emergencyReparentShardOptions.WaitForAllTablets, "wait-for-all-tablets", false, "Should ERS wait for all the tablets to respond. Useful when all the tablets are reachable.")
314
+
EmergencyReparentShard.Flags().BoolVar(&emergencyReparentShardOptions.AllowSplitBrainPromotion, "allow-split-brain-promotion", false, "Allow ERS to proceed when two leading candidates have incomparable Combined GTID positions (suspected split-brain). Off by default. Operator escape hatch — accepts that the losing side's unique GTIDs will become errant.")
312
315
EmergencyReparentShard.Flags().StringSliceVarP(&emergencyReparentShardOptions.IgnoreReplicaAliasStrList, "ignore-replicas", "i", nil, "Comma-separated, repeated list of replica tablet aliases to ignore during the emergency reparent.")
Copy file name to clipboardExpand all lines: go/vt/vtctl/reparent.go
+2Lines changed: 2 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -173,6 +173,7 @@ func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, s
173
173
preventCrossCellPromotion:=subFlags.Bool("prevent_cross_cell_promotion", false, "only promotes a new primary from the same cell as the previous primary")
174
174
ignoreReplicasList:=subFlags.String("ignore_replicas", "", "comma-separated list of replica tablet aliases to ignore during emergency reparent")
175
175
waitForAllTablets:=subFlags.Bool("wait_for_all_tablets", false, "should ERS wait for all the tablets to respond. Useful when all the tablets are reachable")
176
+
allowSplitBrainPromotion:=subFlags.Bool("allow_split_brain_promotion", false, "allow ERS to proceed when two leading candidates have incomparable Combined GTID positions (suspected split-brain); off by default — operator escape hatch")
176
177
177
178
iferr:=subFlags.Parse(args); err!=nil {
178
179
returnerr
@@ -206,6 +207,7 @@ func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, s
"EmergencyReparentSplitBrainOverrides", "Number of times EmergencyReparentShard proceeded despite detecting incomparable Combined GTID positions, because the operator set AllowSplitBrainPromotion=true. The losing side's unique GTIDs will become errant on those tablets after promotion.",
96
+
[]string{"Keyspace", "Shard"},
97
+
)
87
98
)
88
99
89
100
// NewEmergencyReparenter returns a new EmergencyReparenter object, ready to
erp.logger.Warningf("all originally-applied candidates were removed by errant-GTID detection; running second relay-log-apply wait on surviving candidates before promotion")
// We have already removed the tablets with errant GTIDs before calling this function. At this point our winning position must be a
718
-
// superset of all the other valid positions. If that is not the case, then we have a split brain scenario, and we should cancel the ERS
738
+
// superset of all the other valid positions. If that is not the case, then we have a split brain scenario, and we should cancel the ERS —
739
+
// unless AllowSplitBrainPromotion is set, in which case the operator has accepted that the losing side's unique GTIDs will become errant.
719
740
fori, position:=rangetabletPositions {
720
741
if!winningPosition.AtLeast(position) {
721
-
returnnil, nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "split brain detected between servers - %v and %v", winningPrimaryTablet.Alias, validTablets[i].Alias)
742
+
if!opts.AllowSplitBrainPromotion {
743
+
returnnil, nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "split brain detected between servers - %v and %v", winningPrimaryTablet.Alias, validTablets[i].Alias)
744
+
}
745
+
erp.logger.Warningf("AllowSplitBrainPromotion=true: split brain detected between %v and %v — proceeding under operator override; losing side's unique GTIDs will become errant", winningPrimaryTablet.Alias, validTablets[i].Alias)
0 commit comments