[HiCache] L3 storage prefetch lifecycle metrics and cross-tier attribution fixes - #37503
Merged
Merged
Conversation
…ution fixes
Storage (L3) observability had several gaps that made the exported gauges
misleading under real traffic:
- Storage-hit tokens returned by an L3 query were never accounted end to
end, so there was no way to tell how many were actually reused versus
lost to host capacity, transfer shortfall, device capacity, a shrunk
splice base, or a dropped host insert. Add per-request lifecycle
accounting in UnifiedRadixCache with two new counters,
`sglang:storage_prefetch_hit_tokens_total` and
`sglang:storage_prefetch_unfulfilled_tokens_total{reason}`, and resolve
them at admission (cache mode) or at the H2D ack (buffer mode).
- `prefetched_tokens_total` now counts tokens transferred from L3 into host
memory rather than the post-insert delta, and is emitted once per
completed prefetch.
- The per-request device/host/storage cached-token split now uses the
materialized host hit (what init_load_back actually spliced) and the
absolute L3 span start, so L2/L3 attribution survives L3-tail eviction,
declined load-backs, and buffer-mode staging where host memory is only
transport staging rather than a resident L2 tier.
- `hicache_dropped_tokens_total` gains a `write_through_unbacked_eviction`
reason for write-through evictions of device leaves that never completed
their eager backup; the tree core reports the unbacked token count on
the eviction step results. The pool label is now always `kv`.
- Buffer-mode backup intents swept as stale are counted in
`hicache_backup_dropped_tokens_total` instead of vanishing silently.
- `gen_throughput` decays to 0 once decode stats stop arriving instead of
re-exporting a stale window forever, and decode ticks no longer reset the
prefill-owned `cache_hit_rate` gauge to 0.
- `cached_tokens_total{cache_source}` uses the literal `storage` label
instead of a per-backend label, keeping label cardinality bounded.
- Eviction/backup duration histogram defaults are trimmed to a coarse,
widely supported bucket set; the env overrides still widen them.
- Drop the derived `l3_demand_total_tokens` / `l3_sum_rate_*` prefetch
counters and the `occupancy_ratio` snapshot field, which duplicated
information already exported elsewhere.
`get_chunked_req`-style compatibility is preserved: new BasePrefixCache
hooks (`pop_prefetch_loaded_span`, `finish_storage_prefetch_admission`,
`discard_storage_prefetch_accounting`) have no-op defaults so other cache
implementations are unaffected.
Co-authored-by: Zhiqiang Xie <zqx@meta.com>
xiezhq-hermann
requested review from
Ying1123,
alphabetc1,
fzyzcjy,
hanming-lu,
hnyls2002,
huangtingwei9988,
hzh0425,
ispobock,
merrymercy,
sufeng-buaa and
yizhang2077
as code owners
September 2, 2026 01:35
Collaborator
Author
|
/tag-and-rerun-ci |
_FakeTreeCore reuses the real _evict_component_and_detach_lru, which now consults the write-through unbacked-eviction tracker; give the fake the same idle state so the path-state-cap tests run.
Collaborator
Author
|
/tag-and-rerun-ci |
Collaborator
Author
|
/tag-and-rerun-ci |
…thon tree core UnifiedRadixCache only tracks unbacked write-through evictions with the Python UnifiedTreeCore, so the shared suite must skip this assertion under the Rust backend.
Collaborator
Author
|
/tag-and-rerun-ci |
2 similar comments
Collaborator
Author
|
/tag-and-rerun-ci |
Collaborator
Author
|
/tag-and-rerun-ci |
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.
Motivation
L3 (storage) observability in HiCache had several gaps that made the exported gauges misleading under real traffic:
gen_throughputkept re-exporting a stale decode window during long prefill-only stretches, and decode ticks reset the prefill-ownedcache_hit_rategauge to 0.Modifications
UnifiedRadixCache:_record_storage_prefetch_hitstarts accounting at the rank-agreed query result;_resolve_storage_prefetch_tokens/_finish_storage_prefetchretire tokens with a terminal reason (below_threshold,host_capacity,device_capacity,storage_transfer,shrunk,dropped). Cache mode resolves at admission viafinish_storage_prefetch_admission; buffer mode resolves at the H2D ack inBufferModePipeline. Two new counters:sglang:storage_prefetch_hit_tokens_totalandsglang:storage_prefetch_unfulfilled_tokens_total{reason}.prefetched_tokens_totalnow counts tokens transferred from L3 into host memory, emitted once per completed prefetch.Reqgainsstorage_hit_start,host_loaded_length, andhost_hit_is_storage;split_cached_prefix_by_tiertakes the absolute L3 span start and the buffer-mode flag;PrefillAdderrecords the spliced load-back length and accounts admission through a single_account_prefill_cache_admission. The scheduler pops the loaded span via the newpop_prefetch_loaded_spanhook.hicache_dropped_tokens_totalgains reasonwrite_through_unbacked_eviction;UnifiedTreeCorereportsunbacked_tokensonEvictDeviceNextNodeResult/EvictDeviceLeafResult. The pool label is now alwayskv.hicache_backup_dropped_tokens_total.SchedulerMetricsReporter:gen_throughputdecays to 0 after 30s without decode stats; decode ticks no longer zerocache_hit_rate.cached_tokens_total{cache_source}uses the literalstoragelabel (bounded cardinality).l3_demand_total_tokens/l3_sum_rate_*counters and theoccupancy_ratiosnapshot field (redundant with existing exports).New
BasePrefixCachehooks (pop_prefetch_loaded_span,finish_storage_prefetch_admission,discard_storage_prefetch_accounting) have no-op defaults, andStreamingSessionforwards them, so other cache implementations are unaffected.Original commits
719b52246fChecklist
Testing
Ran locally on this branch (CPU):
New tests:
TestCachedPrefixTierAttribution,test_storage_prefetch_fulfillment_resolves_at_admission,test_retracted_storage_prefetch_accounting_is_omitted,test_short_staged_swa_tail_resolves_device_covered_head,test_write_through_eviction_counts_unbacked_tokens,test_positive_hit_with_lost_anchor_is_reported_as_shrunk,TestHiCacheMetrics(recording-metric collectors). Existing prefetch/buffer-mode tests gained lifecycle-metric assertions.Also
black --check,ruff --select=F401,F821,UP037, andpy_compileon all changed files. GPU end-to-end runs were not re-executed from this branch; the change was validated on an internal deployment with L3 storage enabled where the previously stalegen_throughputand zeroedcache_hit_rategauges now read correctly. Relying on CI for the GPU suites.CI States
Latest PR Test (Base): ❌ Run #33739045495
Latest PR Test (Extra): ❌ Run #33739045300
Latest PR Test (AMD ROCm 7.2): ❌ Run #33739045360