Skip to content

fix: add startup jitter to baseProcessor first tick (RUK-134) - #20

Merged
ruko1202 merged 1 commit into
mainfrom
feature/ruk-134
May 25, 2026
Merged

fix: add startup jitter to baseProcessor first tick (RUK-134)#20
ruko1202 merged 1 commit into
mainfrom
feature/ruk-134

Conversation

@ruko1202

Copy link
Copy Markdown
Owner

N task-types × 2 (cleaner + healer) baseProcessors registered in
lockstep all started their time.NewTicker(period) at the same
instant, so every period N=2*task_types DB queries fired at the same
moment — a classic thundering-herd pattern that hurt as the number of
registered task types grew.

Fix: smear the first tick across [0, period) via a uniform random
jitter. Subsequent ticks inherit the offset, so the spread persists
for the lifetime of the process.

Implementation: startupJitter is a field on baseProcessor, defaulting
to defaultStartupJitter (math/rand/v2). Tests override the field to
make timing deterministic — no package-level mutable state.

Also added a guard for misconfigured processPeriod <= 0 — without it
time.NewTicker(0) panics on the next line. Logs and exits cleanly.

Tests cover:

  • defaultStartupJitter randomness (10 buckets × 1000 samples)
  • defaultStartupJitter zero/negative period guard
  • baseProcessor exits cleanly on non-positive period
  • jitter actually delays the first tick (jitter=80ms >> period=10ms)
  • 20 concurrent processors' first-ticks spread > 10% of period
    (flake math: ~2e-18, effectively never)

Note: TD-11 (configurable tick period) — RUK-134 scope mentioned this
but WithHealerPeriod / WithCleanerPeriod already exist in
goque_processor_opts.go, so no work needed there.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

N task-types × 2 (cleaner + healer) baseProcessors registered in
lockstep all started their `time.NewTicker(period)` at the same
instant, so every period N=2*task_types DB queries fired at the same
moment — a classic thundering-herd pattern that hurt as the number of
registered task types grew.

Fix: smear the first tick across [0, period) via a uniform random
jitter. Subsequent ticks inherit the offset, so the spread persists
for the lifetime of the process.

Implementation: startupJitter is a field on baseProcessor, defaulting
to defaultStartupJitter (math/rand/v2). Tests override the field to
make timing deterministic — no package-level mutable state.

Also added a guard for misconfigured processPeriod <= 0 — without it
time.NewTicker(0) panics on the next line. Logs and exits cleanly.

Tests cover:
- defaultStartupJitter randomness (10 buckets × 1000 samples)
- defaultStartupJitter zero/negative period guard
- baseProcessor exits cleanly on non-positive period
- jitter actually delays the first tick (jitter=80ms >> period=10ms)
- 20 concurrent processors' first-ticks spread > 10% of period
  (flake math: ~2e-18, effectively never)

Note: TD-11 (configurable tick period) — RUK-134 scope mentioned this
but WithHealerPeriod / WithCleanerPeriod already exist in
goque_processor_opts.go, so no work needed there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ruko1202
ruko1202 merged commit e069075 into main May 25, 2026
4 checks passed
@ruko1202
ruko1202 deleted the feature/ruk-134 branch May 25, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant