Skip to content

[HiCache] L3 storage prefetch lifecycle metrics and cross-tier attribution fixes - #37503

Merged
xiezhq-hermann merged 6 commits into
mainfrom
feat/hicache-l3-io-query-metrics
Sep 3, 2026
Merged

xiezhq-hermann merged 6 commits into
mainfrom
feat/hicache-l3-io-query-metrics

Conversation

@xiezhq-hermann

@xiezhq-hermann xiezhq-hermann commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Motivation

L3 (storage) observability in HiCache 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. 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.
  • The per-request device/host/storage cached-token split credited host/storage for load-backs that were declined or dropped, and lost L3 attribution whenever the L3-loaded tail was evicted from host before admission. In buffer mode, host memory is transport staging, not a resident L2 tier, and was mis-attributed as L2.
  • gen_throughput kept re-exporting a stale decode window during long prefill-only stretches, and decode ticks reset the prefill-owned cache_hit_rate gauge to 0.
  • Write-through evictions of device leaves that never completed their eager backup were invisible; buffer-mode backup intents swept as stale vanished silently.

Modifications

  • Storage prefetch lifecycle accounting in UnifiedRadixCache: _record_storage_prefetch_hit starts accounting at the rank-agreed query result; _resolve_storage_prefetch_tokens / _finish_storage_prefetch retire tokens with a terminal reason (below_threshold, host_capacity, device_capacity, storage_transfer, shrunk, dropped). Cache mode resolves at admission via finish_storage_prefetch_admission; buffer mode resolves at the H2D ack in BufferModePipeline. Two new counters: sglang:storage_prefetch_hit_tokens_total and sglang:storage_prefetch_unfulfilled_tokens_total{reason}.
  • prefetched_tokens_total now counts tokens transferred from L3 into host memory, emitted once per completed prefetch.
  • Tier attribution: Req gains storage_hit_start, host_loaded_length, and host_hit_is_storage; split_cached_prefix_by_tier takes the absolute L3 span start and the buffer-mode flag; PrefillAdder records the spliced load-back length and accounts admission through a single _account_prefill_cache_admission. The scheduler pops the loaded span via the new pop_prefetch_loaded_span hook.
  • hicache_dropped_tokens_total gains reason write_through_unbacked_eviction; UnifiedTreeCore reports unbacked_tokens on EvictDeviceNextNodeResult / EvictDeviceLeafResult. The pool label is now always kv.
  • Stale buffer-mode backup intents are counted in hicache_backup_dropped_tokens_total.
  • SchedulerMetricsReporter: gen_throughput decays to 0 after 30s without decode stats; decode ticks no longer zero cache_hit_rate.
  • cached_tokens_total{cache_source} uses the literal storage label (bounded cardinality).
  • Eviction/backup duration histogram defaults trimmed to a coarse, widely supported bucket set; env overrides unchanged.
  • Removed derived l3_demand_total_tokens / l3_sum_rate_* counters and the occupancy_ratio snapshot field (redundant with existing exports).

New BasePrefixCache hooks (pop_prefetch_loaded_span, finish_storage_prefetch_admission, discard_storage_prefetch_accounting) have no-op defaults, and StreamingSession forwards them, so other cache implementations are unaffected.

Original commits

  • 719b52246f

Checklist

Testing

Ran locally on this branch (CPU):

pytest test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py \
       test/registered/unit/mem_cache/test_hicache_staged_write_back_dispatch.py \
       test/registered/unit/managers/test_prefill_adder.py \
       test/registered/unit/managers/test_schedule_batch_out_of_place.py \
       test/registered/unit/observability/test_stat_loggers_di.py
# 1244 passed, 1257 skipped (GPU/hybrid-only cases), 76 subtests passed

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, and py_compile on 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 stale gen_throughput and zeroed cache_hit_rate gauges 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

…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

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

xiezhq-hermann and others added 3 commits September 1, 2026 19:13
_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.
@xiezhq-hermann

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

@xiezhq-hermann

Copy link
Copy Markdown
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.
@xiezhq-hermann

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

2 similar comments
@xiezhq-hermann

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

@xiezhq-hermann

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

@xiezhq-hermann
xiezhq-hermann merged commit a480f38 into main Sep 3, 2026
429 of 487 checks passed
@xiezhq-hermann
xiezhq-hermann deleted the feat/hicache-l3-io-query-metrics branch September 3, 2026 23:00
StevenChenSE pushed a commit to StevenChenSE/sglang that referenced this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant