Repository navigation
fix(api): admission level needs sustained pressure to rise — consecutive-tick gate, higher level-1 thresholds, shorter level-1 hold (#9353) - #9512
Merged
Conversation
…ive-tick gate, higher level-1 thresholds, shorter level-1 hold (#9353) In observe mode at idle, single sub-second lag or DB-wait blips raised the admission level and counted thousands of bulk would_shed per hour, so enforce could not be turned on. Root cause: the controller already had a 2-tick rise gate, but its inputs are smoothed over more than one tick. The DB grant-wait p95 used a 3 s window, so one slow grant at idle (p95 = that grant) was re-read on three consecutive ticks. The lag median is repeated unchanged on ticks that read no new monitor sample. A single blip therefore passed the gate. - Rise gate: BREEZE_ADMISSION_RISE_TICKS (default 3, bounds 1..10). A level rises only after N consecutive ticks of fresh evidence. A tick whose only elevated reading is a stale lag median holds the streak. The emergency path is unchanged and still immediate. - The pool gate's grant-wait window is now one tick (3 s -> 1 s), so each tick counts new grants and smoothing is the rise gate's job. - Level-1 thresholds: lag 200 ms / DB wait 150 ms (was 100 / 75). Level 2/3 are unchanged (250/600, 200/600). Thresholds must stay strictly increasing, which rules out 250/200 for level 1. - Level-1 hold: BREEZE_ADMISSION_LEVEL1_HOLD_MS (default 5 s, bounds 1000..60000). Levels 2/3 keep 15 s. Shed bulk already carries a 120-180 s Retry-After, so a long level-1 hold protects nothing. - New counter breeze_admission_rise_suppressed_total{to,trigger} counts the rises the gate held back. would_shed semantics are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying breeze with
|
| Latest commit: |
27e506b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8b7ddda4.breeze-9te.pages.dev |
| Branch Preview URL: | https://fix-9353-admission-level-hys.breeze-9te.pages.dev |
…e state tests, comment accuracy - POOL_WAIT_STATS_WINDOW_MS 1 s -> 1.5 s. A window exactly one tick wide dropped grants that landed between a late tick and the one before it (setInterval never fires early). At 1.5 ticks a grant is read on at most two ticks, still fewer than the default riseTicks of 3. - Controller tests: 1 -> 2 rise under the gate, a stale-lag hold at level 1, lowest-level trigger in mixed streaks, emergency clears an unfinished streak, a stale-only first tick is not reported as suppressed, and a non-default level1HoldMs. - Comments and docs: the Retry-After rationale names the /logs exception and the shipped-vs-W09 agent behaviour; the bounds claim is reworded; the integration stall comment matches its 300 ms interval; the env table spells out the 60 % exit line; the new counter is in the monitoring metrics table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Review run: /pr-review-toolkit:review-pr with code-reviewer, pr-test-analyzer and comment-analyzer, run in parallel. silent-failure-hunter and type-design were skipped: no error-handling changes, and only small additive types. Findings: 0 critical, 0 important. 3 code suggestions, 8 test gaps and 6 comment/doc items were raised. All of them were addressed in 27e506b except the ones below.
Tests:
Status: review-clean, awaiting maintainer merge. Before flipping |
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.
Summary
BREEZE_ADMISSION_RISE_TICKS(default 3) consecutive ticks of fresh evidence above its threshold. A single blip never raises it. The emergency path (BREEZE_ADMISSION_EMERGENCY_MS) is unchanged and still immediate.BREEZE_ADMISSION_LEVEL1_HOLD_MS(default 5 s, was the shared 15 s). Levels 2 and 3 keep 15 s.breeze_admission_rise_suppressed_total{to,trigger}counts each rise the gate held back.would_shed/shedsemantics are unchanged, so the before/after comparison the issue asks for stays valid.Root cause (why a tick gate alone would not have fixed it)
The controller already had a 2-tick rise gate (
ADMISSION_ESCALATE_TICKS = 2). Single blips still got through because both inputs are smoothed over more than one tick:trigger=db, dbWait=110ms, dbWaiting=0, dbInUse=0. Raising the gate to 3 ticks would still have let every one of these through. The new testa single slow pool grant at idle never raises the leveluses a real gate and the real sampler. It fails with the 3 s window even atriseTicks=3(mutation-checked).lag=251ms, peakLag=21..31ms: the median was high while this tick's newest sample was low.Fix: the grant-wait window is now 1.5 ticks (
POOL_WAIT_STATS_WINDOW_MS3 s → 1.5 s). A grant is read on at most two ticks, fewer than the 3-tick gate, and the extra half tick covers late ticks, so grants are never dropped between ticks. The sampler now reportslagFresh. A tick whose only elevated reading is a stale lag median holds the streak: it neither advances nor resets it. Smoothing is now the rise gate's job, and the gate counts real evidence.Decisions made on the brief (please check)
250,250,600would be rejected. It would also make level 1 unreachable, because every rise would go straight to shedding standard traffic. 200 / 150 is twice the old entry and the highest round values that still leave a level-1 band. To get the literal numbers, raise level 2 too, for exampleBREEZE_ADMISSION_LAG_MS=250,400,600andBREEZE_ADMISSION_DB_WAIT_MS=200,350,600.Linked Issues
Closes #9353
Refs #8139
Merge Danger
Door: two-way. Every default can be overridden by env. Setting
BREEZE_ADMISSION_LAG_MS=100,250,600 BREEZE_ADMISSION_DB_WAIT_MS=75,200,600 BREEZE_ADMISSION_RISE_TICKS=2 BREEZE_ADMISSION_LEVEL1_HOLD_MS=15000restores the old controller, except for the 1.5 s grant-wait window and the stale-lag hold. A revert restores everything.Blast radius: agent bulk/standard admission on every API instance. In
observemode, which both regions run today, it changes counters only. Underenforceit decides which agent uploads get 503. Shedding now starts ~3 s into sustained pressure instead of ~2 s. Stalls ≥ 1 s still go straight to level 3.Prod re-check (before flipping
enforce)After this deploys, run on both regions:
increase(breeze_admission_decisions_total{outcome="would_shed"}[1h])The target is near zero at idle. Also check
increase(breeze_admission_rise_suppressed_total[1h])to see how many blips the gate absorbed.enforcestays off until both look right.Type of Change
Testing
Red first: 40 new and updated assertions failed against
mainbefore the implementation.cd apps/api && npx vitest run src/services/admission src/middleware/agentAdmission src/db/poolAdmission.test.ts src/routes/agentWs.test.ts src/system/connections src/config: 66 files / 2045 tests passed.would_shed, new stays at 0 with 0would_shed.level1HoldMs.lagFreshturns the 3 stale-lag tests red.vitest.integration.config.ts, real pg on a per-worktree test stack):admissionControl.integration.test.ts3/3 passed, re-run after the review fixes. Its lag scenario was raised from 150 ms to 230 ms blocking slices, because 150 ms no longer reaches the new level-1 entry. The 150 ms version was confirmed to time out under the new defaults. The test stack was torn down afterwards.pnpm exec tsc --build apps/api/tsconfig.tests.jsonexit 0, 0 errors. Plaintsc --noEmitran out of memory at 12 GB (exit 134), the known trap, so this build-mode config is what CI runs..env.example,internalEnvVars.tsand the docs env table are updated. The env inventory and compose-coverage suites are green (included in the run above).Checklist
🤖 Generated with Claude Code