Case 1 Β· MLOps: Monitoring and Recovery

#vllm#monitoring#dagster#recovery

A batch job translates and summarizes 24K articles on a local LLM served by vLLM. Running it surfaced two problems: the vLLM dashboard showed the GPU recomputing prompts it should have cached, and a single failed summary re-ran its whole article.

Efficiency
A prefix-cache hit rate of only 24.7% on the vLLM dashboard. Parallel processing and restructured prompts raised it to 45.7%.
Recovery
A failed summary re-ran its whole article, and failures were buried in the result files. One Dagster step per agent call made each failure visible, and only the failed step re-runs.
Result
Prefill compute βˆ’28%, prefill time βˆ’10%, TTFT βˆ’8%. Recovery tokens βˆ’37% versus re-running failed articles.

vLLM monitoring Β· Dagster step-level re-runs

Monitoring found both problems; restructuring the pipeline fixed them

Prefix-cache hit rate

Tokens spent recovering 20 failures

βˆ’28%Prefill compute
βˆ’10%Prefill time
βˆ’8%Time to first token
βˆ’37%Recovery tokens

Efficiency: the prefix cache

vLLM exports Prometheus metrics such as prefix-cache queries and hits, cached prompt tokens, prefill time and time to first token. Together with GPU health, they feed a Grafana dashboard I watch during batch runs. During the summarization batch the prefix-cache hit rate sat at 24.7%, even though many requests share the same article text.

vLLM automatic prefix caching β€” vLLM keys each KV-cache block by a hash of its tokens and every token before them. A request that shares a prompt prefix with an earlier one reuses those blocks and skips their prefill, the pass over the prompt before the first output token. Unused blocks are evicted least-recently-used first, so request order decides how much is reused.

The same prompt prefix was recomputed

Many requests share the same article text.

Each one prefilled it again instead of reusing it from the cache.

Found while monitoring vLLM and GPU health

The dashboard showed a prefix-cache hit rate of only 24.7%.

Shared prefixes were evicted before they could be reused.

Parallel processing and prompt restructuring

Changed the pipeline to parallel processing.

Restructured the prompts to share a longer common prefix.

ResultSame requests, same model, less GPU compute β€” prefill compute βˆ’28%, prefill time βˆ’10%, TTFT βˆ’8%

Fix: parallel processing and prompt restructuring

I changed the pipeline to parallel processing and restructured the prompts, so requests that share a prefix reuse it from the cache. The requests and the model stayed the same.

Same requests, before vs after

MetricBeforeAfter
Prefix-cache hit rate24.7%45.7%
Prompt tokens computed18.24M13.20M
Total prefill time3,259 s2,923 s
Time to first token108 ms99 ms

Recovery: re-run the failed step

If one step failed, the old batch script re-ran the whole article, redoing the translation and summaries that had already succeeded.

One failed summary re-ran the whole article

Usually one language failed, yet the re-run redid the translation and the good summaries.

Failures sat inside each article's result; finding them meant scanning.

The recovery unit was the article

The batch script could only resume per article.

Per-step outputs could not be reused.

One agent call, one Dagster step

A branch per article, a step per agent call, article ID in the step key.

Good upstream outputs load from storage; only the failed step re-runs.

ResultLess time finding failures, fewer tokens recovering them

Comparing recovery granularity

Switch the recovery granularity to see the difference. Each tile is one article: left half English summary, right half Korean summary.

Recovery method

The Korean summary failed for 20 of 100 articles. Every English summary succeeded. The granularity at which you recover these failures sets the cost.

Re-running the whole batch regenerates the 80 successful articles and every successful English summary. You pay the original cost a second time.

Re-running only the failed articles still regenerates their 20 successful English summaries. This is how the batch script worked, since it could only resume per article.

Dagster re-runs only the 20 failed steps and loads the 20 successful English summaries from storage.

Recovering 20 failed stepsRequestsTokensTime
Re-run whole batch1,9151.61M446 s
Re-run failed articles338276k89 s
Dagster Β· failed steps only205174k56 s