{"record":{"id":"3dbe8cb6234720f8","repo":"vitessio/vitess","slug":"ignoring-higher-good-rate-of-v-because-we-assume","errorCode":null,"errorMessage":"ignoring higher good rate of %v because we assume that the known maximum capacity (currently at %v) can only degrade","messagePattern":"ignoring higher good rate of (.+?) because we assume that the known maximum capacity \\(currently at (.+?)\\) can only degrade","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"go/vt/throttler/memory.go","lineNumber":87,"sourceCode":"\tm.badRateIncrease = badRateIncrease\n}\n\n// int64Slice is used to sort int64 slices.\ntype int64Slice []int64\n\nfunc (a int64Slice) Len() int           { return len(a) }\nfunc (a int64Slice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\nfunc (a int64Slice) Less(i, j int) bool { return a[i] < a[j] }\n\nfunc searchInt64s(a []int64, x int64) int {\n\treturn sort.Search(len(a), func(i int) bool { return a[i] >= x })\n}\n\nfunc (m *memory) markGood(rate int64) error {\n\trate = m.roundDown(rate)\n\n\tif lowestBad := m.lowestBad(); lowestBad != 0 && rate > lowestBad {\n\t\treturn fmt.Errorf(\"ignoring higher good rate of %v because we assume that the known maximum capacity (currently at %v) can only degrade\", rate, lowestBad)\n\t}\n\n\t// Skip rates which already exist.\n\ti := searchInt64s(m.good, rate)\n\tif i < len(m.good) && m.good[i] == rate {\n\t\treturn nil\n\t}\n\n\tm.good = append(m.good, rate)\n\tsort.Sort(int64Slice(m.good))\n\treturn nil\n}\n\nfunc (m *memory) markBad(rate int64, now time.Time) error {\n\t// Bad rates are rounded up instead of down to not be too extreme on the\n\t// reduction and account for some margin of error.\n\trate = m.roundUp(rate)\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/throttler/memory.go#L69-L105","documentation":"The throttler's memory module refuses to record a 'good' rate higher than the lowest bad rate ever observed, on the assumption that maximum capacity can only degrade, not improve. The proposed good rate is ignored and this error is returned (logged by the throttler).","triggerScenarios":"markCurrentRateAsBadOrGood calls memory.markGood(rate) when the current rate exceeds m.lowestBad(); typically right after a rate was marked bad and the observed rate bounces above it.","commonSituations":"Replica catch-up causes a sudden throughput spike above the previously recorded bad rate; benchmark-style bursts right after a throttler rate-limit event.","solutions":["No action required — this is expected self-protection; the throttler keeps the conservative limit.","If rates legitimately recovered, keep reporting rates; bad values age out and limits can be raised via the throttler API.","Restart/recreate the throttler if the recorded bad rate is stale and no longer representative."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if lowestBad := mem.LowestBadForTest(); lowestBad != 0 && rate > lowestBad {\n    // expect rejection; skip reporting\n}","typeGuard":null,"tryCatchPattern":"if err := mem.markGood(rate); err != nil {\n    log.Debug(\"good rate ignored by memory module\", slog.Any(\"error\", err))\n}","preventionTips":["Treat this as informational — log at debug level, don't alert.","Don't construct rate histories that oscillate around the bad rate in tests without expecting this.","Rely on sustained rate reports rather than single spikes to raise limits."],"tags":["go","throttler","rate-limiting"],"backgroundTag":"throttler-rate-rejected","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}