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

  1. Do not call recalculateRate with an empty replicationLagRecord; skip or return early at the call site.
  2. Verify the record producer always sets at least Stats/Time/Target before publishing the record.
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/a8b1077a2d3f521f. Report an issue: GitHub.