Skip to content

fix: refresh SGLang radix recency on splits and cached re-inserts - #581

Closed
PeaBrane wants to merge 1 commit into
ai-dynamo:mainfrom
PeaBrane:fix/sglang-radix-recency
Closed

PeaBrane wants to merge 1 commit into
ai-dynamo:mainfrom
PeaBrane:fix/sglang-radix-recency

Conversation

@PeaBrane

Copy link
Copy Markdown
Contributor

Why and what changed

A partial radix match or insert splits a cached edge without refreshing its retained suffix. Finishing a request whose entire sequence is already cached can also return before refreshing the path. Both leave recently accessed KV older than an unrelated prompt and change the next eviction victim.

Refresh a matched child before splitting it, and refresh the start path even when an insertion has no new pages. Apply this to token and page-hash entrypoints.

Review map

  • Risk level: medium; changes SGLang cache eviction order.
  • Start with: crates/core/src/engine/cache/radix_cache.rs.
  • Public or serialized contract changed: no.
  • Compatibility or rollback concern: replay eviction order intentionally changes; the radix layout and capacity accounting stay the same.

Evidence

  • Tests: cargo test -p aisimulate-core — 2,446 passed, zero failures, 1 ignored. This full suite ran before the final comment/legal-only changes; final-head focused results follow.
  • Final-head focused regressions: cargo test -p aisimulate-core --lib -- engine::cache::radix_cache::tests — 40 passed, zero failures. Nightly formatting and packaged legal-file checks passed.
  • Fast CI: not run.
  • Full CI: not run.
  • CodeRabbit reviewed commit: not reviewed.
  • Codex reviewed commit: c15ba51; bounded gap review found no material concern.
  • Negative or boundary cases: page sizes 1, 4 and 64; locked and unlocked partial matches; token and hash insertion; reinsertion without new pages. The 18 new cases fail on unchanged main: 18 expected assertion failures, 22 passes, exit 101 after successful compilation.
  • Expected-value derivation: a controlled clock makes prompt A older than B, then accesses A; eviction must remove B while preserving A.
  • Before/after output, trace, benchmark, or golden diff: the tests assert exact evicted page identities and remaining prefix lengths.

Modeling or data provenance

The behavior follows SGLang v0.5.20 (radix matching and insertion). Tests use synthetic token sequences and a controlled clock; no measured performance claim.

Tracking

None.

This pull request and its description were written by Isaac.

SGLang refreshes every radix node that a match or insert walks,
including a child whose edge is only partially matched and then split,
so the retained suffix and the new prefix node are both most recently
used (legacy `_match_prefix_helper` and `_insert_helper` in
python/sglang/srt/mem_cache/radix_cache.py L731-L739 and L794-L813 at
v0.5.20; the default unified tree core touches the walked child and
refreshes it again in `_split_node`, unified_cache/unified_tree_core.py
L1196-L1200 and L1396). Re-inserting a sequence whose pages are all
cached walks and refreshes the same path.

The simulator split a partially matched child without refreshing it,
so its suffix kept the old timestamp, and an insert with no new pages
returned before refreshing the path. Refresh the child before splitting
it on the match and insert paths, and refresh the start path even when
the inserted suffix is empty. Read-only admission matching and Belady's
page-wise eviction are unchanged.

Signed-off-by: Rudy Pei <yanrpei@gmail.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@PeaBrane
PeaBrane requested review from a team as code owners October 24, 2026 00:38
@copy-pr-bot

copy-pr-bot Bot commented Oct 24, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between bd2e1b3 and c15ba51.

📒 Files selected for processing: 3
  • THIRD_PARTY_NOTICES.md
  • crates/core/src/engine/cache/radix_cache.rs
  • python/aisimulate/THIRD_PARTY_NOTICES.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

📜 Recent review details
🧰 Additional context used
📚 Code guidelines: 2
AGENTS.md — configured
REVIEW.md — configured
📓 Path-based instructions: 5
Review scheduler state transitions and event ordering, including cancel, preempt, drain, retry, duplicate, empty, and terminal paths.

⚙️ CodeRabbit configuration file

Files:

  • crates/core/src/engine/cache/radix_cache.rs
Verify upstream provenance, license text, collection and overlay hashes, local modification notices, and generated derivative records remain complete and internally consistent.

⚙️ CodeRabbit configuration file

Files:

  • THIRD_PARTY_NOTICES.md
Read REVIEW.md before commenting.

⚙️ CodeRabbit configuration file

Files:

  • THIRD_PARTY_NOTICES.md
  • python/aisimulate/THIRD_PARTY_NOTICES.md
  • crates/core/src/engine/cache/radix_cache.rs
Source excerpt: Keep run logs, investigation notes, and benchmark results out of `docs/`; measured evidence belongs under `benchmarks/evidence/`.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • THIRD_PARTY_NOTICES.md
  • python/aisimulate/THIRD_PARTY_NOTICES.md
  • crates/core/src/engine/cache/radix_cache.rs
Source excerpt: Binary parquet changes require a machine-readable summary of keys, shapes, units, row counts, coverage, and anomalies plus proof that a real consumer reaches the new data.

📄 CodeRabbit inference engine (REVIEW.md)

Files:

  • THIRD_PARTY_NOTICES.md
  • python/aisimulate/THIRD_PARTY_NOTICES.md
  • crates/core/src/engine/cache/radix_cache.rs
🪛 markdownlint-cli2 (0.23.3)
THIRD_PARTY_NOTICES.md

[warning] 1455-1455: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

python/aisimulate/THIRD_PARTY_NOTICES.md

[warning] 1455-1455: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

🔀 Multi-repo context ai-dynamo/dynamo, ai-dynamo/aiconfigurator

Linked repositories findings

ai-dynamo/dynamo

  • Dynamo’s shared-cache router independently computes aligned page hashes and checks physical object presence; it does not call RadixCache or insert_page_hash_suffix, nor track access timestamps in this path. [::ai-dynamo/dynamo::lib/llm/src/kv_router/shared_cache.rs:472-520]
  • The documented HiCache contract is based on store/remove events and per-tier residency, with no recency API exposed to Dynamo. [::ai-dynamo/dynamo::docs/fern/pages/developer-guide/knowledge-base/modular-components/backends/sglang/hicache.md:58-96]
  • Dynamo contains separate radix-tree/LRU benchmarking infrastructure, but the changed APIs were not referenced by the repository search. [::ai-dynamo/dynamo::lib/bench/kv_router/approximate_lru_bench.rs:18-45]

ai-dynamo/aiconfigurator

  • The compatibility/reference collectors disable SGLang’s radix cache, so they do not exercise the changed split or cached-reinsert recency behavior. [::ai-dynamo/aiconfigurator::collector/sglang/collect_attn.py:112]
  • Multiple collector configurations also set disable_radix_cache=True. [::ai-dynamo/aiconfigurator::collector/sglang/collect_mla_module.py:1101]
📝 Summary

Risk: Moderate. The three areas for human attention are:

  1. Split recency: Confirm partial matches and inserts refresh the matched child before splitting, so the retained suffix receives the intended timestamp.
  2. Cached re-inserts: Confirm token and page-hash insertion paths refresh the existing path when no new pages are added, including empty aligned suffixes.
  3. Eviction interactions: Confirm updated timestamps remain consistent with locked-node handling and eviction ordering. Read-only admission matching and Belady’s page-wise eviction are reported unchanged.

Changes

  • RadixCache now refreshes matched children before splitting them during prefix matching and insertion.
  • insert_page_hash_suffix touches the starting path even when it inserts no hashes.
  • Added regression cases for partial matches, partial inserts, and cached re-inserts across page sizes and insertion paths.
  • Added a third-party notice for the SGLang v0.5.20 adaptation, including the pinned source revision and Apache-2.0 attribution.
  • No public API changes are reported.

Evidence and readiness

The supplied test report says the focused radix-cache tests passed with 40 tests and no failures. It also reports 18 new cases fail on unchanged main. The broader cargo test -p aisimulate-core result predates final comment/legal-only changes. Fast CI and full CI were not run.

These results support the technical intent, but do not establish full CI status. Review severity counts are unavailable because no current review findings were supplied. Technical quality and merge readiness therefore remain separate: the targeted evidence is positive, while full CI and current review findings are missing.

RadixCache now refreshes access timestamps for partially matched edges and for paths traversed during empty-suffix insertion. Tests cover partial matching, insertion, and reinsertion across page sizes and insertion paths. Third-party notices document the adapted SGLang source and license.

Changes: Radix-cache access recency

Layer / File(s) Summary
Refresh cache paths and test eviction ordering
crates/core/src/engine/cache/radix_cache.rs, THIRD_PARTY_NOTICES.md, python/aisimulate/THIRD_PARTY_NOTICES.md
RadixCache touches matched children before deciding whether to split them. Empty-suffix insertion refreshes the starting path. Tests cover partial matches and inserts, plus reinsertion without new pages, for page sizes 1, 4, and 64. The notices identify the SGLang v0.5.20 source, commit, license, and AISimulate adaptations.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to c15ba

The recency changes have no identified merge-blocking issue; normal checks remain appropriate.

Pre-merge checks: Passed 8
Check name Status Explanation
Title check Passed The title "fix: refresh SGLang radix recency on splits and cached re-inserts" names the actual behavioral change: recency refresh during partial-edge splits and cached-prefix reinserts in the radix ca…
Description check Passed The description follows the repository template. It explains the problem and behavior change, names the riskiest file, and states that the public contract is unchanged. Evidence is detailed and includ…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Cross-Layer Contract Passed PASS: The PR changes only Rust cache behavior and synchronized third-party notices. crates/core/src/engine/cache/mod.rs:6 keeps radix_cache crate-private, and the changed method signatures remain …
Modeling And Data Evidence Passed The PR provides provenance and reproducible behavioral evidence. THIRD_PARTY_NOTICES.md identifies SGLang v0.5.20, commit 94602c9c2b7cbdb8efd5c52802dac6a1c180089e, and the adapted source paths. Th…
Compatibility Boundaries Passed No compatibility boundary changed. The PR changes only private crates/core/src/engine/cache/radix_cache.rs behavior and two synchronized notice files; cache::radix_cache remains pub(crate) (crat…
Review Evidence Passed The PR description names the relevant local commands and exact results: the full cargo test -p aisimulate-core run and the focused radix-cache test run. It records boundary coverage for page sizes 1…
Full details: Title check

Explanation

The title "fix: refresh SGLang radix recency on splits and cached re-inserts" names the actual behavioral change: recency refresh during partial-edge splits and cached-prefix reinserts in the radix cache. It matches the radix_cache.rs changes and the PR objectives. It is specific and avoids vague wording, so it passes the title requirements.

Full details: Description check

Explanation

The description follows the repository template. It explains the problem and behavior change, names the riskiest file, and states that the public contract is unchanged. Evidence is detailed and includes test commands, results, boundary cases, and a controlled-clock expected-value derivation. The Modeling or data provenance section gives the SGLang source and states that the tests use synthetic data. Minor gaps: the Review map does not state a CodeRabbit review (it says 'not reviewed', which is acceptable), the Fast CI and Full CI fields say 'not run' without explanation, and the Closes field says 'None' instead of being left as a plain link. The SPDX header from the template is omitted, which is acceptable for a PR body. Overall the description is mostly complete.

Full details: Cross-Layer Contract

Explanation

PASS: The PR changes only Rust cache behavior and synchronized third-party notices. crates/core/src/engine/cache/mod.rs:6 keeps radix_cache crate-private, and the changed method signatures remain internal; no schema, CLI, serialization, or public output contract changed. Rust consumers are limited to sglang_backend.rs calls at lines 370, 598, 633, and 663. The new behavior is covered in radix_cache.rs:1644-1710 for token and hashed insertion, locked and unlocked matching, empty-suffix reinsertion, and page sizes 1, 4, and 64. Both THIRD_PARTY_NOTICES.md files document the adapted source and behavior. Repository-wide search found no Python or CLI consumer of the changed cache APIs.

Full details: Modeling And Data Evidence

Explanation

The PR provides provenance and reproducible behavioral evidence. THIRD_PARTY_NOTICES.md identifies SGLang v0.5.20, commit 94602c9c2b7cbdb8efd5c52802dac6a1c180089e, and the adapted source paths. The changed tests use a controlled clock, fixed page sizes, and exact expected evicted page identities and retained prefix lengths (crates/core/src/engine/cache/radix_cache.rs:1597-1710). The PR description also reports deterministic before/after results: the new cases fail on the base revision and pass on the final revision. This is explained golden-output evidence, not binary-only validation.

Full details: Compatibility Boundaries

Explanation

No compatibility boundary changed. The PR changes only private crates/core/src/engine/cache/radix_cache.rs behavior and two synchronized notice files; cache::radix_cache remains pub(crate) (crates/core/src/engine/cache/mod.rs:6), with no changed schemas, bindings, versions, manifests, or defaults. The root and wheel notice copies are byte-identical. The release contract still defines exactly one aisimulate wheel and one aisimulate-core crate (docs/ci/release.md:8-28), and the release validator still enforces that manifest and artifact set (scripts/release/build_release_artifacts.py:45-97, 155-161). No Dynamo dependency or integration was added. This matches the downstream boundary, where Dynamo consumes separately published aisimulate-core and a pinned aisimulate wheel (ai-dynamo/dynamo@d043ae0, Cargo.toml:53-55; container/deps/requirements.aisimulate.txt:4-6).

Full details: Review Evidence

Explanation

The PR description names the relevant local commands and exact results: the full cargo test -p aisimulate-core run and the focused radix-cache test run. It records boundary coverage for page sizes 1, 4, and 64, locked and unlocked matches, token and hash insertion, and cached reinsertion, plus negative validation against unchanged main. It separates local test evidence from hosted CI by stating that Fast CI and Full CI were not run. It does not cite any inactive nested workflow as hosted CI evidence, and it makes no unsupported fake-runner, parity, or production claim.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@PeaBrane

Copy link
Copy Markdown
Contributor Author

Superseded by #583, which now contains this change together with the related SGLang cache lifecycle, eviction, reporting, and KV-event corrections. The consolidated branch has passed local core and public-API validation.

@PeaBrane PeaBrane closed this Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant