Skip to content

feat(tier-normalization): normalize device tier names and default unknown tier weight to 0 - #695

Open
Alex-ai-future wants to merge 5 commits into
llm-d:mainfrom
Alex-ai-future:fix/tier-normalization
Open

feat(tier-normalization): normalize device tier names and default unknown tier weight to 0#695
Alex-ai-future wants to merge 5 commits into
llm-d:mainfrom
Alex-ai-future:fix/tier-normalization

Conversation

@Alex-ai-future

Copy link
Copy Markdown
Contributor

What this PR does

Addresses two problems with device tier handling in the KV cache scorer:

Problem 1: Unknown tier defaults to weight 1.0 → silently inflates scores

fillMaxWeights falls back to weight = 1.0 for any DeviceTier not in the configured KVCacheBackendConfig. With the default config (gpu=1.0, cpu=0.8), new tiers introduced by vLLM (e.g. fs, obj) get the same score as GPU:

Pod state Before After
GPU(1.0) + FS(unknown) max(1.0, 1.0) = 1.0 max(1.0, 0) = 1.0 ✅
CPU(0.8) + FS(unknown) max(0.8, 1.0) = 1.0 max(0.8, 0) = 0.8 ✅
Only FS(unknown) 1.0 ❌ (same as GPU) 0 + warning ⚠️

Fix: Default unknown tier weight to 0.0 with a one-time warning per unknown tier. Users must explicitly configure new tiers to participate in routing.

Problem 2: Tier name mismatch → store/remove don't match

Different components use different names for the same physical tier:

vLLM BlockStored:       medium="FS"               → PodEntry{pod, "fs"}
PVC Evictor BlockRemoved: medium="SHARED_STORAGE"  → PodEntry{pod, "shared_storage"}

Same physical tier, two different PodEntries. Evict of "shared_storage" doesn't match the "fs" store entry → stale entries remain.

Fix: Added a tier alias registry (Config.TierAliases) that normalizes alternative medium names to canonical names before constructing PodEntries. Built-in defaults: shared_storage → fs, object_store → obj.

Changes

File Description
pkg/kvcache/kvblock_scorer.go fillMaxWeights default 0 + one-time warning per unknown tier
pkg/kvevents/pool.go Config.TierAliases field, normalizeTier method, default aliases
pkg/kvcache/kvblock_scorer_test.go Tests for unknown tier weight 0 behavior
pkg/kvevents/pool_test.go Tests for tier alias normalization (FS/SHARED_STORAGE → fs)
docs/configuration.md Document tierAliases config and unknown tier behavior

Backward Compatibility

  • Existing configs with gpu and cpu weights are unaffected.
  • Unknown tiers change from silent 1.0 → silent 0 + warning. This is safer: pods with only unknown-tier entries become invisible to the router until the user explicitly configures them.
  • Built-in aliases (shared_storage → fs, object_store → obj) are additive; users can override via config.

@github-actions github-actions Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jul 8, 2026
…nown tier weight to 0

Two problems addressed:
1. Unknown device tiers (e.g. fs, obj) defaulted to weight 1.0, silently
   inflating pod scores above known-tier pods like CPU(0.8). Now defaults
   to 0 with a one-time warning per unknown tier.
2. Different components use different names for the same physical tier
   (e.g. vLLM emits FS, PVC Evictor emits SHARED_STORAGE). Added a tier
   alias registry that normalizes these to canonical names so store/remove
   events build equal PodEntries and match correctly during eviction.

Changes:
- pkg/kvcache/kvblock_scorer.go: fillMaxWeights default 0 + warning
- pkg/kvevents/pool.go: Config.TierAliases, normalizeTier method
- pkg/kvcache/kvblock_scorer_test.go: tests for unknown tier weight 0
- pkg/kvevents/pool_test.go: tests for tier alias normalization
- docs/configuration.md: document tierAliases and unknown tier behavior

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@Alex-ai-future
Alex-ai-future force-pushed the fix/tier-normalization branch from b2f092d to ae0a32a Compare July 8, 2026 06:50
Resolves gocritic typeDefFirst lint violation.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
…sertions

Previously the test ignored verification results (_ = reqKey). Now it:
1. Verifies PodEntry{pod-1, fs} exists after BlockStored with FS
2. Verifies the entry is evicted after BlockRemoved with SHARED_STORAGE
3. Confirms no pods remain for the requestKey after eviction

Signed-off-by: Alex <alex.tech.lab@outlook.com>
Resolves ineffassign and staticcheck lint failures.

Signed-off-by: Alex <alex.tech.lab@outlook.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves KV-cache scoring and event deduplication correctness by (1) preventing unknown device tiers from inflating pod scores and (2) normalizing storage tier names so store/remove events refer to the same canonical tier.

Changes:

  • Default unknown device-tier weights to 0.0 in the KV-cache scorer and emit a one-time warning per unknown tier.
  • Add configurable tier alias normalization (with built-in defaults) so differing medium names (e.g., SHARED_STORAGE vs FS) map to the same tier.
  • Add/extend unit tests and update configuration documentation for the new behaviors.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/kvevents/pool.go Adds tier alias registry and normalizes tiers during event processing.
pkg/kvevents/pool_test.go Adds tests validating alias normalization behavior.
pkg/kvcache/kvblock_scorer.go Changes unknown-tier default weight to 0 and logs a one-time warning per tier.
pkg/kvcache/kvblock_scorer_test.go Adds tests ensuring unknown tiers do not inflate scores.
docs/configuration.md Documents unknown-tier weight behavior and the new tierAliases configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/kvevents/pool.go Outdated
Comment on lines +143 to +150
// Build tier aliases: start with defaults, then merge user-provided overrides
tierAliases := make(map[string]string)
for k, v := range defaultTierAliases {
tierAliases[k] = v
}
for k, v := range cfg.TierAliases {
tierAliases[k] = v
}
Comment thread pkg/kvevents/pool_test.go Outdated
Comment on lines +1187 to +1191
cfg := &Config{
TierAliases: map[string]string{
"custom_tier": "fs",
},
}
Comment on lines +28 to +30
// unknownTierWarned tracks which unknown device tiers have already been warned
// about, so we don't spam the logs on every block.
var unknownTierWarned sync.Map
- Lowercase user-provided alias keys/values so normalizeTier matches
  regardless of how users write them in config (e.g. SHARED_STORAGE → fs)
- Use DefaultConfig() in TestNormalizeTierMethod to avoid zero Concurrency
  which would cause queue index panic

Addresses Copilot review comments.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants