Part of the #621 testing work for the v0.7.0 tracker (#606). Builds on the replayed-/metrics e2e fixture landing with #382.
Problem
inference-perf hardcodes the Prometheus metric names it queries for every supported model server, and nothing tells us when an upstream server renames, removes, or re-namespaces one:
vllm_client.py: ~25 names under vllm: (vllm:num_requests_waiting, vllm:time_to_first_token_seconds, vllm:request_time_per_output_token_seconds, vllm:inter_token_latency_seconds, vllm:prompt_tokens, vllm:generation_tokens, vllm:kv_cache_usage_perc, ...)
sglang_client.py: 9 names under sglang:
tgi_client.py: 6 names under the tgi_ prefix
The failure is silent by construction. A stale name produces no error: the PromQL matches nothing, the query returns an empty result, and the affected field in summary_prometheus_metrics.json is absent or zero. A user reading the report cannot distinguish "the server did not report this" from "we asked for a name that no longer exists."
We already have one instance in flight: #382 updates sglang:cache_hit_rate to sglang:token_usage, a rename that was caught by hand rather than by CI.
Why it matters
This is the #564 family. Nothing fails, a number is just missing or wrong, and it stays that way until someone notices a blank column. The blast radius is every server-metrics section we publish, and the probability rises over time because all three servers version independently of us.
TGI is the sharpest case: it is the least active of the three upstreams and the one we exercise least, so a rename there could sit undetected for a full release cycle.
Proposed shape
A scheduled job whose output is a fixture-refresh PR, not a test failure:
- On a schedule (weekly is enough), start the latest release of each supported server: vLLM, SGLang, TGI.
- Scrape
/metrics once and write the response to the checked-in fixture for that server, stamped with the server version it came from.
- If the fixture is unchanged, exit quietly. If it changed, open a PR with the new dump.
That PR then runs the normal merge-blocking e2e test, which replays the fixture through a real Prometheus and asserts the assembled report. A metric we no longer recognize shows up there as a report field going empty against the golden. So drift detection needs no new assertion machinery: it reuses the blocking test, and the failure lands on one attributable PR that someone owns instead of on every open PR at once.
Two things that keep this cheap:
- No benchmark run is required. All three servers register their metric families at engine init, so a bare startup plus a single scrape is enough to see the names.
- No new test to maintain. The job's only logic is start, scrape, diff, open PR.
Explicitly not merge-blocking
Deliberate. If latest upstream gated merges, a rename would turn every open PR red simultaneously, including the PR that fixes it. A moving external dependency cannot sit in the merge gate. Advisory and periodic is the correct contract here, and it is why this row does not gate a release even though it lives in the live tier.
Update (Aug 17): for vLLM this changed with #697: every release in e2e/vllm_releases.txt, including latest-x86_64, gates at merge. The "fixing PR is red too" premise does not hold once the table and declarations live in the tree. The scheduled, non-gating shape stays the plan for SGLang and TGI, which have no CPU tier.
Relationship to existing work
Known limit
This catches renames, removals, and re-namespacing, because those show up as a name appearing or disappearing from the dump. It does not catch a metric that keeps its name while changing units or semantics. Nothing short of value-level review catches that, and it is the same silent-wrongness family this issue is trying to shrink.
Part of the #621 testing work for the v0.7.0 tracker (#606). Builds on the replayed-
/metricse2e fixture landing with #382.Problem
inference-perf hardcodes the Prometheus metric names it queries for every supported model server, and nothing tells us when an upstream server renames, removes, or re-namespaces one:
vllm_client.py: ~25 names undervllm:(vllm:num_requests_waiting,vllm:time_to_first_token_seconds,vllm:request_time_per_output_token_seconds,vllm:inter_token_latency_seconds,vllm:prompt_tokens,vllm:generation_tokens,vllm:kv_cache_usage_perc, ...)sglang_client.py: 9 names undersglang:tgi_client.py: 6 names under thetgi_prefixThe failure is silent by construction. A stale name produces no error: the PromQL matches nothing, the query returns an empty result, and the affected field in
summary_prometheus_metrics.jsonis absent or zero. A user reading the report cannot distinguish "the server did not report this" from "we asked for a name that no longer exists."We already have one instance in flight: #382 updates
sglang:cache_hit_ratetosglang:token_usage, a rename that was caught by hand rather than by CI.Why it matters
This is the #564 family. Nothing fails, a number is just missing or wrong, and it stays that way until someone notices a blank column. The blast radius is every server-metrics section we publish, and the probability rises over time because all three servers version independently of us.
TGI is the sharpest case: it is the least active of the three upstreams and the one we exercise least, so a rename there could sit undetected for a full release cycle.
Proposed shape
A scheduled job whose output is a fixture-refresh PR, not a test failure:
/metricsonce and write the response to the checked-in fixture for that server, stamped with the server version it came from.That PR then runs the normal merge-blocking e2e test, which replays the fixture through a real Prometheus and asserts the assembled report. A metric we no longer recognize shows up there as a report field going empty against the golden. So drift detection needs no new assertion machinery: it reuses the blocking test, and the failure lands on one attributable PR that someone owns instead of on every open PR at once.
Two things that keep this cheap:
Explicitly not merge-blocking
Deliberate. If
latestupstream gated merges, a rename would turn every open PR red simultaneously, including the PR that fixes it. A moving external dependency cannot sit in the merge gate. Advisory and periodic is the correct contract here, and it is why this row does not gate a release even though it lives in the live tier.Update (Aug 17): for vLLM this changed with #697: every release in
e2e/vllm_releases.txt, includinglatest-x86_64, gates at merge. The "fixing PR is red too" premise does not hold once the table and declarations live in the tree. The scheduled, non-gating shape stays the plan for SGLang and TGI, which have no CPU tier.Relationship to existing work
/metricsdump replayed through a real Prometheus, with the assembled report asserted against a golden. That is what makes this job's PR meaningful. This issue should not block Cleanup Prometheus Metric Querying #382.scripts/check_coverage_regression.pyrunspytest --cov=inference_perf tests/and does not includee2e/.schedule:is the lower-friction path since e2e already lives there; a prow periodic in kubernetes/test-infra is more idiomatic for k8s-sigs but adds a cross-repo review.Known limit
This catches renames, removals, and re-namespacing, because those show up as a name appearing or disappearing from the dump. It does not catch a metric that keeps its name while changing units or semantics. Nothing short of value-level review catches that, and it is the same silent-wrongness family this issue is trying to shrink.