vitessio/vitess · error
rate recalculation was triggered with a zero replication lag
Error message
rate recalculation was triggered with a zero replication lag record
What it means
recalculateRate drives the throttler's rate state machine from a replication lag record. A zero-valued replicationLagRecord means no real measurement reached it, so continuing would corrupt state; the function panics immediately. It guards against the rate recalculation loop being triggered spuriously.
Source
Thrown at go/vt/throttler/max_replication_lag_module.go:303
default:
panic(fmt.Sprintf("BUG: invalid TabletType forwarded: %v", tabletType))
}
}
func (m *MaxReplicationLagModule) getNSlowestReplicasConfig(lagRecord replicationLagRecord) int {
switch lagRecord.Target.TabletType {
case topodatapb.TabletType_REPLICA:
return int(m.config.IgnoreNSlowestReplicas)
case topodatapb.TabletType_RDONLY:
return int(m.config.IgnoreNSlowestRdonlys)
default:
panic(fmt.Sprintf("BUG: invalid TabletType forwarded: %v", lagRecord))
}
}
func (m *MaxReplicationLagModule) recalculateRate(lagRecordNow replicationLagRecord) {
if lagRecordNow.isZero() {
panic("rate recalculation was triggered with a zero replication lag record")
}
// Protect against nil stats
if lagRecordNow.Stats == nil {
return
}
now := lagRecordNow.time
lagNow := lagRecordNow.lag()
m.memory.ageBadRate(now)
r := Result{
Now: now,
RateChange: unchangedRate,
lastRateChange: m.lastRateChange,
OldState: m.currentState,
NewState: m.currentState,View on GitHub (pinned to 01a25a7d17)
Solutions
- Do not call recalculateRate with an empty replicationLagRecord; skip or return early at the call site.
- Verify the record producer always sets at least Stats/Time/Target before publishing the record.
- In tests, build a valid record via the package's helpers rather than replicationLagRecord{}.
Example fix
// before
m.recalculateRate(replicationLagRecord{}) // panics
// after
if !lagRecordNow.isZero() {
m.recalculateRate(lagRecordNow)
} Defensive patterns
Strategy: validation
Validate before calling
if lagRecordNow.isZero() {
return // never forward the zero record to recalculateRate
}
m.recalculateRate(lagRecordNow) Prevention
- Never pass replicationLagRecord{} or sentinel zero records into the throttler
- Populate Time/Stats/Target on every record before publishing
- In tests, use the package's record helpers instead of empty structs
When it happens
Trigger: recalculateRate invoked (directly from processRecord or recursively via the state machine) with the zero value of replicationLagRecord — e.g. a record struct constructed without any fields populated, or a sentinel zero record passed through the recalculation channel.
Common situations: Test code calling recalculateRate with an empty struct; a code path inserting a placeholder record instead of skipping it when no lag sample is available.
Related errors
- BUG: cannot add record because it is already covered by a pr
- BUG: cannot add record because it does not start at the begi
- BUG: invalid TabletType forwarded: %v
- BUG: invalid state: %v
- BUG: cannot compare states: %v and %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/a8b1077a2d3f521f.
Report an issue: GitHub.