docs: explain and surface the prometheus-budget closing point - #374
Merged
Conversation
shimib
requested review from
RishabhSaini,
ahg-g,
evacchi and
jtechapps
as code owners
July 29, 2026 20:18
shimib
added a commit
that referenced
this pull request
Jul 29, 2026
Signed-off-by: Shimi Bandiel <shimib@google.com>
max_concurrency is a per-ready-pod divisor: the prometheus-budget gate closes only once load reaches ready_pods * max_concurrency * (1 - baseline). Nothing said so, and nothing reported the resolved number, so a max_concurrency the pool can never reach left the gate permanently open with every batch request dispatching regardless of live traffic -- silently, which is the exact failure the gate exists to prevent. Log the resolved closing point when the gate is built: "prometheus-budget gate configured" pool=... maxConcurrency=100 baseline=0.05 closesAtLoadPerReadyPod=95 Document max_concurrency as per-pod capacity in the README, the CreateGate doc comment, and the e2e guide, with two ways to pick a value: match the EPP saturation detector's MaxConcurrency so the async gate and EPP agree on when the pool is full, or measure the per-pod peak of vllm:num_requests_running under the load you consider saturated. The guide keeps max_concurrency: "100". It is reachable in the setup the guide actually deploys -- a single Qwen3-0.6B replica driven by the guide's own 200-worker load test, which the guide already records as reaching 200 concurrent -- and it matches the EPP default that the llm-d optimized-baseline recipe leaves unset. Lowering it would contradict the expected values the guide states. What was missing was any indication that the number is per-pod and must be re-derived for a different model, pool size, or workload, so that is what this adds, in the values file and in a new "Size max_concurrency for your pool" section. Fixes #363 Signed-off-by: Shimi Bandiel <shimib@google.com>
Signed-off-by: Shimi Bandiel <shimib@google.com>
shimib
force-pushed
the
fix/budget-gate-max-concurrency
branch
from
July 29, 2026 21:14
20f8375 to
9cc87da
Compare
jtechapps
approved these changes
Jul 29, 2026
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.
Fixes #363
max_concurrencyon theprometheus-budgetgate is a per-ready-pod divisor. The gate closes only once load reaches:Nothing said that, and nothing reported the resolved number. A
max_concurrencythe pool can never reach leaves the gate permanently open — every batch request dispatches regardless of live traffic, which is the exact failure the gate exists to prevent — and it fails silently.What changed
Surface the closing point.
CreateGatenow logs it when it builds aprometheus-budgetgate:That is the "startup log line reporting the resolved closing point" the issue asks for. It uses the per-pod form because
ready_podsis a live Prometheus reading, not known at construction time — but per-pod is the number you compare againstvllm:num_requests_running / ready_pods, so it is the actionable half.Document it as per-pod capacity, in three places that all previously described it as an opaque constant:
CreateGatedoc comment,max_concurrencysubsection in the README'sprometheus-budgetreference,max_concurrencyfor your pool section in the e2e guide, with akubectl logs | grepfor the line above and a PromQL query for deriving the value from your own peak.Two ways to pick a value are documented: match the EPP saturation detector's
MaxConcurrency(so the async gate and the EPP agree on when the pool is full), or measure the per-pod peak ofvllm:num_requests_runningunder the load you consider saturated.On the shipped value
The issue offers two fixes — drop the value, or "better, document it … and tell the reader to derive it". I took the second, and deliberately left
max_concurrency: "100"indocs/guides/e2e-deploy/llm-d-async-values.yaml, because on the setup the guide actually deploys the value is reachable:heywith 200 workers, and the guide already recordsExpected: vllm:num_requests_running = 200 (saturated)andExpected: value = -1 (200 running / 100 max).guidellmreaches ~110. Both close the gate today.100matches the EPP saturation detector default, and the llm-doptimized-baselinerouter recipe does not configure the detector — so the two components currently agree. Changing one side to8would desynchronise them.Dropping it to
8would therefore falsify the guide's own stated expectations and the* 100divisors in its verification queries, to fix a mis-sizing that only appears once the reader swaps in a different model. The real defect is that the guide presents the number with no indication that it is per-pod or that it must be re-derived — which is what this PR fixes, in the values file comment and the new section. Happy to lower it as well if you'd rather the shipped default err small; that is a one-line change plus the expected values in the load-test sections.Note on the "no metric" half of the finding
Once #372 lands,
async_gate_metric_value/async_gate_metric_thresholdexpose the live D and the baseline as a joinable pair, so an unreachable threshold is visible as a series that never approaches its threshold. This PR covers the log-line half.Verification
The Go change is one
log.Log.WithName(...).Info(...)call using the package-level controller-runtime logger already used elsewhere in this package (metric_dispatch_gate.go,cascade_metric_source.go) and inpkg/redis/quota_gate.go. No API change, no behavior change to gate evaluation. The values file was re-parsed to confirm the comment-only edit leavesgate_paramsbyte-identical:{"pool"=>"optimized-baseline", "max_concurrency"=>"100", "baseline"=>"0.05"}.