Skip to content

feat(observability): report real prefix-cache query/hit counters in /metrics - #953

Open
1571859588 wants to merge 2 commits into
pegainfer-project:mainfrom
1571859588:feat/observability-prefix-cache-metrics
Open

feat(observability): report real prefix-cache query/hit counters in /metrics#953
1571859588 wants to merge 2 commits into
pegainfer-project:mainfrom
1571859588:feat/observability-prefix-cache-metrics

Conversation

@1571859588

Copy link
Copy Markdown

Summary

Threads prefix-cache query/hit counters from the qwen3 scheduler through
SchedulerMetrics into the vLLM SchedulerStats.prefix_cache_stats surface,
so Prometheus /metrics no longer reports zeros for prefix-cache hit rate.

  • pegainfer-qwen3: accumulate per-step prefix_queries/prefix_hits in
    StepEffects (one query per first-chunk request; hits = cached_tokens),
    fold into cumulative counters on the scheduler, expose via metrics().
  • pegainfer-frontend: add prefix_cache_queries/prefix_cache_hits to
    SchedulerMetrics and map them to PrefixCacheStats in the vLLM bridge.

Relation to #814 / #669 / #603

This supersedes #814 (which was opened from feat/sim-frontend-prefix-cache-metrics,
head 7f24658e, against the pre-rename tree and went CONFLICTING due to
#841 PegaInfer rename + #824 kv-store refactor). Per the maintainer note
on #814, this is the single canonical PR for the prefix-cache reporting surface.

Scope split to avoid two parallel implementations:

Verified

  • cargo test -p pegainfer-frontend --lib — 65 passed (incl. bridge
    prefix_cache_stats mapping).
  • cargo clippy + cargo check --workspace --lib — clean.

Note: a full qwen3 A100 e2e was blocked in this local environment by a
rdma-mummy-sys bindgen issue (missing vendored rdma-core-mummy headers) that
is unrelated to this change and does not touch rdma code; upstream CI with the
full rdma toolchain will validate the qwen3 build.

…metrics

Thread prefix-cache query/hit counters from the qwen3 scheduler through
SchedulerMetrics into the vLLM SchedulerStats.prefix_cache_stats surface,
so Prometheus /metrics no longer reads zeros for prefix cache hit rate.

- pegainfer-qwen3: accumulate per-step prefix_queries/prefix_hits in
  StepEffects (one query per first-chunk request; hits = cached_tokens),
  fold into cumulative counters on the scheduler, expose via metrics().
- pegainfer-frontend: add prefix_cache_queries/hits to SchedulerMetrics and
  map them to PrefixCacheStats in the vLLM bridge.

This complements the cached_tokens usage path (TokenEvent::Scheduled) that
upstream already landed for pegainfer-project#603; it covers the /metrics consumer only.

Verified: pegainfer-frontend --lib tests pass (65); cargo clippy and
cargo check --workspace --lib clean. qwen3 A100 e2e blocked locally by a
rdma-mummy-sys bindgen environment issue unrelated to this change.
Copilot AI lite review requested due to automatic review settings August 23, 2026 04:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab0234f87f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pegainfer-frontend/src/vllm/bridge.rs Outdated
Comment on lines +604 to +605
queries: snapshot.prefix_cache_queries,
hits: snapshot.prefix_cache_hits,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Diff prefix-cache totals before exporting counters

For Qwen3's stepped bridge, SchedulerMetrics carries monotonic totals and dispatch_step sends scheduler_stats_from(&self.scheduler.metrics()) on every output batch; vLLM's Prometheus logger increments prefix_cache_queries/hits by the values in each SchedulerStats. Once any cached request has run, every subsequent token batch re-adds the same cumulative totals, so /metrics overcounts prefix-cache traffic until the process restarts. Please compute per-send deltas here, like the existing spec-decode path does.

Useful? React with 👍 / 👎.

Comment on lines +116 to +117
effects.prefix_queries += 1;
effects.prefix_hits += result.cached_tokens as u64;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count prefix-cache queries in tokens

For any prompt longer than one token, this makes the query denominator request-granularity (+1) while prefix_hits is token-granularity (cached_tokens). vLLM's prefix-cache counters are meant to be compared as hit tokens / queried tokens, so a repeated ~1900-token prompt would report roughly 1888 hits over 1 query and produce impossible hit rates above 100%. Increment queries by the queried/cacheable prompt token count instead.

Useful? React with 👍 / 👎.

@xiaguan

xiaguan commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Please fix the token-vs-request query-unit mismatch and cumulative-counter double counting, then provide an exact-head, multi-batch scheduler-to-/metrics E2E result showing stable non-zero deltas across scrapes; the PR currently reports that Qwen3 E2E was blocked, so no E2E result has been provided.

…r-send deltas

Two review bugs on the prefix-cache `/metrics` surface, both fixed:

1. Unit mismatch (queries vs hits). Previously `prefix_queries += 1` (a
   request) while `prefix_hits += cached_tokens` (a token count), so
   `hit_rate = hits/queries` could exceed 100%. Now both are TOKEN-granular,
   matching vLLM's `PrefixCacheStats`: `prefix_queries` counts the prompt
   tokens looked up in the cache and `prefix_hits` counts the cached tokens.
   Because cached <= prompt, `hits <= queries` and the rate stays in [0, 1].
   Guarded by the existing `prefill_pos == 0` check so each request is counted
   exactly once (no double counting across chunked prefill).

2. Cumulative overcount in Prometheus. The scheduler holds running totals, but
   `dispatch_step` / `publish_scheduler_stats` shipped that running total on
   *every* token batch, and the frontend adds each `SchedulerStats` value into
   its `prefix_cache_*_total` counters — so a cached request re-added the whole
   history on every subsequent batch until restart. Now the bridge ships
   per-send DELTAS (cur - last), mirroring the existing spec-decode path:
   `prefix_cache_delta()` in bridge.rs, with `last_prefix_*` state in both the
   legacy `publish_scheduler_stats` loop and the stepped bridge (AtomicU64,
   since `dispatch_step` takes `&self`).

Adds a `FakeExecutor` prefix-hit hook, a multi-batch/multi-scrape qwen3
scheduler test (`prefix_cache_metrics_stable_across_batches_and_scrapes`,
token-granular assertions), and a frontend test
(`prefix_cache_stats_are_per_interval_deltas_not_running_totals`) that proves
the bridge ships the interval delta, not the running total.

Signed-off-by: yuntaonie <1571859588@qq.com>
@1571859588

Copy link
Copy Markdown
Author

Summary

Fixes two issues flagged on the prefix-cache /metrics counters:

Token-vs-request query-unit mismatch (pegainfer-qwen3/src/scheduler/resolve.rs): prefix_queries was incremented per-request (+= 1) while prefix_hits was token-granular (+= result.cached_tokens), so repeated prompts reported >100% hit rates. Both are now token-granular: prefix_queries += req.prompt_tokens.len(), prefix_hits += result.cached_tokens (cached_tokens ≤ prompt_tokens ⇒ rate ∈ [0,1]).
Cumulative-counter double counting (pegainfer-frontend/src/vllm/bridge.rs + bridge/stepped.rs): dispatch_step shipped the scheduler's monotonic totals every batch; vLLM's Prometheus increments *_total by each value, over-counting until restart. Now it ships per-send deltas (prefix_cache_delta(last, cur)), mirroring the existing spec-decode delta path. Last-state is tracked in both the legacy publish_scheduler_stats loop and the &self stepped dispatch_step (via AtomicU64).

Verification

cargo test --release -p pegainfer-frontend --lib vllm::bridge → 14/14 passed, including prefix_cache_stats_are_per_interval_deltas_not_running_totals (interval 1 ships (100,37); interval 2 after totals reach (200,74) ships delta (100,37) not (200,74); idle ships (0,0)).
Scheduler→metrics token-granular path verified via a line-for-line replica: 4 batches × 3 reqs, prompt=64/hit=37 → totals (768,444), rate 0.578, stable per-batch deltas (+192,+111), no overcount.

Note on E2E

The live A100 /metrics multi-batch scrape requested in review could not be produced in this environment: the qwen3 build chain pulls rdma-mummy-sys, whose bindgen (0.66) generates _address fields under the local toolchain, breaking that third-party crate's compile — unrelated to this change. The overcount behavior is directly covered by the passing bridge delta test. If CI runs on this head, the bridge test goes green; I can supply a real /metrics scrape snapshot once a qwen3-buildable environment is available.

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.

3 participants