{"record":{"id":"1d9f4531520c6488","repo":"vitessio/vitess","slug":"bug-cannot-add-record-because-it-is-already-cover","errorCode":null,"errorMessage":"BUG: cannot add record because it is already covered by a previous entry. record: %v next expected interval start: %v","messagePattern":"BUG: cannot add record because it is already covered by a previous entry\\. record: (.+?) next expected interval start: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/throttler/interval_history.go","lineNumber":51,"sourceCode":"type intervalHistory struct {\n\trecords           []record\n\tinterval          time.Duration\n\tnextIntervalStart time.Time\n}\n\nfunc newIntervalHistory(capacity int64, interval time.Duration) *intervalHistory {\n\treturn &intervalHistory{\n\t\trecords:  make([]record, 0, capacity),\n\t\tinterval: interval,\n\t}\n}\n\n// add\n// It is up to the programmer to ensure that two add() calls do not cover the\n// same interval.\nfunc (h *intervalHistory) add(record record) {\n\tif record.time.Before(h.nextIntervalStart) {\n\t\tpanic(fmt.Sprintf(\"BUG: cannot add record because it is already covered by a previous entry. record: %v next expected interval start: %v\", record, h.nextIntervalStart))\n\t}\n\tif !record.time.Truncate(h.interval).Equal(record.time) {\n\t\tpanic(fmt.Sprintf(\"BUG: cannot add record because it does not start at the beginning of the interval. record: %v\", record))\n\t}\n\t// TODO(mberlin): Bound the list.\n\th.records = append(h.records, record)\n\th.nextIntervalStart = record.time.Add(h.interval)\n}\n\n// average returns the average value across all observations which span\n// the range [from, to).\n// Partially included observations are accounted by their included fraction.\n// Missing observations are assumed with the value zero.\nfunc (h *intervalHistory) average(from, to time.Time) float64 {\n\t// Search only entries whose time of observation is in [start, end).\n\t// Example: [from, to) = [1.5s, 2.5s) => [start, end) = [1s, 2s)\n\tstart := from.Truncate(h.interval)\n\tend := to.Truncate(h.interval)","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/throttler/interval_history.go#L33-L69","documentation":"intervalHistory.add maintains a strictly ordered series of fixed-width time intervals. If a record's time is earlier than h.nextIntervalStart, it would overlap an already-recorded interval, so add panics with this BUG message. It is an internal invariant violation, not a user-facing error.","triggerScenarios":"Calling add with a record whose time predates the next expected interval start — e.g. adding two records whose times fall in the same interval, or adding records out of chronological order.","commonSituations":"Feeding throttler replication-lag records that were buffered and arrive out of order; clock adjustments (NTP step back) producing a record time in the past; test code adding records with duplicate timestamps.","solutions":["Ensure records are added in monotonically increasing time order, one per interval.","Drop or merge any record whose time is before nextIntervalStart instead of calling add for it (the caller in the throttler already filters; check for a bypass).","If system clock jumps are suspected, harden the producer to skip stale samples after a backward clock step."],"exampleFix":"// before\nh.add(record{time: t, lag: lag}) // t may overlap prior interval\n// after\nif !t.Before(h.nextIntervalStart) && t.Truncate(h.interval).Equal(t) {\n  h.add(record{time: t, lag: lag})\n}","handlingStrategy":"validation","validationCode":"func canAdd(h *intervalHistory, t time.Time) bool {\n  // exposed via the package's own test seam; replicate the invariant:\n  return !t.Before(h.nextIntervalStart)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Feed records in strictly increasing time order, one per interval","Drop stale samples produced after a backward clock step (NTP)","In tests, derive record times from a base truncated to the interval"],"tags":["go","panic","throttler","time-series","invariant-violation"],"backgroundTag":"out-of-order-timestamp","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}