Repository navigation
fix(checkworker): stop the rate limiter starving long-period checks - #609
Merged
Merged
Conversation
A rate-limited job is deferred by a full period and keeps its old effective_scheduled_at, so it wins the next contended claim. That rotates the deficit among 1-minute checks, but an hourly check gets one attempt per hour, always at the same UID-hash second. When frequent siblings drain the org's bucket just before that second, claim priority is worth nothing and the check never runs again. Seen in prod: an org at 10.5 checks/min against a 10/min cap had two hourly TLS checks with no result for 27 hours. A job the gate already deferred may now borrow one token, taking the bucket into debt down to one burst below zero. The next on-time arrivals are deferred instead, so the deficit keeps rotating, and refill repays the debt so the long-run rate stays at the cap. Both gates (in-process worker and agent claim) go through the new ReserveJobExecution. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #609 +/- ##
==========================================
- Coverage 83.36% 83.36% -0.01%
==========================================
Files 1238 1238
Lines 127020 127026 +6
==========================================
+ Hits 105894 105896 +2
- Misses 21117 21121 +4
Partials 9 9
🚀 New features to boost your workflow:
|
Merged
fclairamb
added a commit
that referenced
this pull request
Oct 19, 2026
🤖 I have created a release *beep* *boop* --- ## [0.40.1](v0.40.0...v0.40.1) (2026-10-19) ### Bug Fixes * **checkworker:** stop the rate limiter starving long-period checks ([#609](#609)) ([3295a20](3295a20)) * **dash0:** style the HTTP timing chart like the response time chart ([#611](#611)) ([c5264d2](c5264d2)) * **deps:** update dependency @hookform/resolvers to v6 ([#610](#610)) ([8474852](8474852)) * **deps:** update go dependencies (non-major) ([#612](#612)) ([45512d4](45512d4)) * **deps:** update module github.com/arran4/golang-ical to v0.3.8 ([#605](#605)) ([8c5ef41](8c5ef41)) * **deps:** update module github.com/fergusstrange/embedded-postgres to v2 ([#613](#613)) ([0f1b50c](0f1b50c)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The prod watchdog reported two hourly SSL checks of one org showing "No data" for 27 hours while their region was healthy.
The gravelines worker logged
Check execution rate-limited; deferring to next period(limit 10) at every hourly attempt. The org schedules about 10.5 checks/min against the SaaS cap of 10/min.The fair rotation of spec 2026-08-26-02 keeps a deferred job's
effective_scheduled_at, so it wins the next contended claim. That works for 1-minute checks (they all ran 1139 to 1426 times in 24h). An hourly check gets one attempt per hour, always at the same second. Those two land one second after a burst of 1-minute siblings has drained the bucket, so they lose every time.Fix
CheckJob.RateLimitDeferred:scheduled_atabout a period ahead ofeffective_scheduled_at) may borrow one token.entitlements.ReserveJobExecution.Tests
TestDeferredJobBorrowsSoLongPeriodChecksRunsimulates 12 hours of the prod org second by second, on the real bucket, with the phases read fromcheck_jobs.TestReserveDeferredCheckExecutionDebtIsBoundedcovers the debt floor and its repayment.TestCheckJobRateLimitDeferredcovers the predicate: on time, cost offset, tier credit, deferred.Under extreme overload (demand far above the cap) the debt floor can still be reached, and then the same arrival-time lottery applies. That is out of scope here.
🤖 Generated with Claude Code