feat(prefixcacheaffinity): derive the load gate bar from the request - #6
Open
Lucas-Fernandes-Martins wants to merge 1 commit into
Open
feat(prefixcacheaffinity): derive the load gate bar from the request#6Lucas-Fernandes-Martins wants to merge 1 commit into
Lucas-Fernandes-Martins wants to merge 1 commit into
Conversation
Adds penaltyMode: matchedTokens as an opt-in alternative to the fixed
maxTTFTPenaltyMs. Default is unchanged.
The gate is the only mechanism that can release a cache pin once the
filter has made one, and today it thresholds against a constant:
bestStickyTTFT - bestNonStickyTTFT > maxTTFTPenaltyMs
TTFT_ms = inFlightTokens / peakPrefillThroughput * 1000
That constant has to be chosen per deployment and goes stale as the
workload moves. Measured across 8 llm-d deployments the correct value
spans 143ms to 4092ms, and on one of them it moved ~16x within a day as
mean prompt length changed. peakPrefillThroughput compounds it: measured
peak spans 743 to 27261 tok/s against a 15928 default documented as
"calibrated for Qwen 32B on 2x H100".
matchedTokens asks the question the constant is standing in for. Staying
pinned costs the extra queued work; leaving costs re-prefilling whatever
the sticky endpoint already holds:
inFlight(bestSticky) - inFlight(bestNonSticky) > MatchBlocks * BlockSizeTokens
Both sides are token counts, so no throughput constant enters the
decision. Both come from PrefixCacheMatchInfo and InFlightLoad, which
the filter already reads - MatchBlocks a few lines earlier for the
affinity score - so no new plumbing.
maxTTFTPenaltyMs is kept as an absolute ceiling in this mode: a request
with a large enough cached prefix would otherwise justify an unbounded
queue.
Rejected at startup when combined with ttftSource: latencyPredictor,
which returns milliseconds from a model with no token interpretation.
An endpoint with no readable match attribute yields zero matched tokens
and skips the test rather than releasing on a missing signal.
bestTTFT is refactored onto bestByTTFT, which also returns the winning
endpoint; the break-even test needs per-endpoint state rather than a
scalar.
Lucas-Fernandes-Martins
marked this pull request as ready for review
August 5, 2026 20:45
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.
Opt-in
penaltyMode: matchedTokens. Default behaviour is unchanged.Why
The TTFT load gate is the only thing that can release a cache pin once the affinity filter has made one:
Both inputs are constants chosen at deploy time, and both are hard to choose:
The right threshold is per-deployment and moves. Measured across 8 llm-d deployments, the break-even value spans 143 ms → 4,092 ms. On glm-5-2 it shifted ~16× within a day as mean prompt length went from ~5k to ~78k tokens.
peakPrefillThroughputcompounds it. Measured peak spans 743 → 27,261 tok/s against a15928default whose own comment says "Calibrated for Qwen 32B on 2x H100 80GB (TP=2), vLLM 0.19". It is a straight multiplier on the effective token threshold, so a wrong value silently rescales a threshold someone else picked.In practice the default barely fires. On glm-5-2 the gate released 0.6% of requests (4 of 702 sampled scheduling decisions) while 17.2% stayed pinned to an endpoint 5–18 s of prefill more loaded than an available alternative. Reconstructed gate input on that deployment: p50 40 ms, p90 6,459 ms, p99 15,148 ms — against a bar of 18,000 ms.
What this does
Ask the question the constant stands in for. Staying pinned costs the extra queued work; leaving costs re-prefilling whatever the sticky endpoint already holds:
Both sides are token counts, so no throughput constant enters the decision. Both come from
PrefixCacheMatchInfoandInFlightLoad, which the filter already consumes —MatchBlocks()a few lines earlier for the affinity score — so there is no new plumbing and no new dependency.The bar now scales with how much the request would actually forfeit, which is what makes it survive a workload shift.
Design choices worth reviewing
maxTTFTPenaltyMsis retained as an absolute ceiling in this mode. A request with a large enough cached prefix would otherwise justify an unbounded queue. Set it to 0 to disable the ceiling.ttftSource: latencyPredictor. That path returns milliseconds from a model with no token interpretation, so the two sides are not commensurable. Failing fast beats silently comparing unlike units.bestTTFTrefactored ontobestByTTFT, which also returns the winning endpoint — the break-even test needs per-endpoint state, not a scalar.bestTTFTis kept as a thin wrapper so existing call sites are untouched.Test plan
go build ./...clean;go test ./pkg/... ./apix/...0 failures.Nine new tests in
plugin_test.go:KeepsPinWhenGapBelowMatchedTokensReleasesPinWhenGapExceedsMatchedTokensScalesWithCacheValueCeilingStillAppliesNoMatchInfoIsNoOpStaticModeUnchangedDefaultPenaltyModeIsStaticMatchedTokensRejectsLatencyPredictorRejectsUnknownPenaltyModeStatus
Draft, on the fork, not proposed upstream yet. Two things I would want settled first:
maxTTFTPenaltyMsrather than sit beside it. The mode exists as opt-in specifically to avoid changing anyone's behaviour on upgrade, but the long-term win is deleting two calibration knobs, not adding a third.Measurements come from
~/mistral/sync_perf/(gate_dist.pyreconstructs the gate input from EPP scorer logs;recommend.pymeasures PPT and break-even per deployment).