Skip to content

fix(kv-router): fail closed on unrecognized KV event media - #12456

Merged
Change72 merged 8 commits into
ai-dynamo:mainfrom
Change72:codex/unrecognized-media-fail-closed
Aug 10, 2026
Merged

fix(kv-router): fail closed on unrecognized KV event media#12456
Change72 merged 8 commits into
ai-dynamo:mainfrom
Change72:codex/unrecognized-media-fail-closed

Conversation

@Change72

@Change72 Change72 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

dynamo was fail-open on KV-event medium strings it doesn't recognize: convert_event fell back to the Device tier via from_kv_medium_or_default, so an unrecognized medium was indexed on the G1 primary radix tree — and separately flowed through the salted cache-namespace normalizer state and was tracked as G1 by both consolidator ingresses. This PR makes all three paths fail closed.

Concrete trigger: vLLM v0.26.0 (dynamo's current pin) emits FS / OBJ media (present since vLLM #47923; unified to STORAGE only in 0.27+ via #48123). dynamo doesn't recognize FS / OBJ, so today they hit all three holes. This is the follow-up promised in #11571:

Hardening dynamo's handling of unrecognized media (fail-closed instead of defaulting to Device) is a separate follow-up PR — it is a pre-existing latent issue on main, independent of this change.

Behavior

medium before → after
absent / GPU / CPU / STORAGE / other recognized unchanged
unrecognized (e.g. v0.26.0 FS / OBJ, future strings) router: fell back to Device → indexed on G1 → dropped in conversion (rate-limited warn), now a backstop; normalizer: polluted salted-namespace state, and a later conversion drop burned a next_event_id (false engine-drop metric) → rejected in preprocess (unknown_medium filter reason); no event id burned; consolidator: tracked as G1 → dropped

Changes

  • convert_event: drop unrecognized media with a rate-limited warning (was a silent Device fallback). Removes the now-unused StorageTier::from_kv_medium_or_default. This drop is kept as a defensive backstop for direct callers that bypass preprocess.
  • preprocess_with_reason: reject unrecognized media with a new unknown_medium filter reason instead of letting them reach conversion. Filtering here — exactly like the feat(kv-router): route vLLM STORAGE KV events to the Disk tier with locality gating #11571 locality gate — means the LLM ZMQ listener records an intentional filter rather than accepting the event, incrementing next_event_id, and only dropping it in conversion; that id gap is what the event processor misreads as an engine drop (engines_dropped_events). Recognized lower-tier media (STORAGE → Disk/External) still bypass normalizer state; Device and HostPinned events (GPU, CPU offload feat(kv-router): Route vLLM CPU KV events to HostPinned and count lower-tier applies #10368) still flow through the normalizer so their salted namespaces keep propagating.
  • Both consolidator ingresses (kvbm-consolidator, LLM kv_consolidator): gate unrecognized media out of the G1-only tracker.

The normalizer and consolidator conditions are intentionally different (normalizer keeps CPU on-path for salted propagation; the consolidator is G1-only and drops CPU), so there is no shared predicate.

Out of scope: locality semantics (settled in #11571), the CPU/HostPinned normalizer path (#10368), any FS / OBJ → tier mapping (dynamo deliberately does not consume them — this only flips them from fail-open to fail-closed), and removal/refcount semantics (#11930).

Validation

All commands on a Linux workstation, all green:

cargo test -p dynamo-kv-router --lib
cargo test -p dynamo-kv-router --features standalone-indexer --lib
cargo test -p dynamo-llm --lib kv_router::publisher
cargo test -p dynamo-llm --lib block_manager::kv_consolidator
cargo test -p kvbm-consolidator
cargo fmt --all -- --check
cargo clippy --no-deps --all-targets -- -D warnings

New/updated tests:

  • preprocess_with_reason rejects FS / OBJ / XYZ as unknown_medium (store + remove) while recognized media pass.
  • an FS event reusing a salted GPU hash is rejected in preprocess (so it never touches namespace state) and the salted GPU chain keeps inheriting the namespace.
  • listener-level GPU → FS → GPU (real ZMQ publisher): the two GPU stores land on consecutive event ids (0, 1), proving the filtered FS store burns no next_event_id (no false engine drop).
  • convert_event still drops FS / OBJ / XYZ as a backstop (store + remove, any locality); both consolidator ingresses drop FS.

AI assistance

Implemented with AI assistance (Cursor); design, scoping, and review were human-directed.

Summary by CodeRabbit

  • New Features

    • Added support for vLLM disk offloading through the documented storage tiering configuration.
    • Clarified that STORAGE events map to Dynamo’s Disk tier.
  • Bug Fixes

    • Unknown or unsupported storage media are now rejected instead of silently defaulting to device storage.
    • Invalid events no longer consume event IDs or alter namespace state.
    • Updated event filtering to consistently accept only supported device-tier events where required.
  • Documentation

    • Documented rejection of legacy FS and OBJ event types.

Unknown medium strings previously fell back to the Device tier in
convert_event (indexing them on the G1 primary radix tree), flowed
through the salted cache-namespace normalizer state, and were tracked
as G1 by both consolidator ingresses. vLLM 0.26.0 ships FS/OBJ media
(pre-#48123 wire) that hit all three paths.

Drop unrecognized media in conversion with a rate-limited warning,
bypass normalizer state for them (Device/HostPinned still flow through
so CPU-offload salted propagation is preserved), and gate both
consolidator ingresses; remove the now-unused from_kv_medium_or_default.
Follow-up promised in ai-dynamo#11571.

Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator July 30, 2026 23:10 — with GitHub Actions Inactive
@Change72
Change72 temporarily deployed to external_collaborator July 30, 2026 23:10 — with GitHub Actions Inactive
@github-actions github-actions Bot added the fix label Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot added the external-contribution Pull request is from an external contributor label Jul 30, 2026
@datadog-official

datadog-official Bot commented Jul 30, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 40.82% (-1.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 7e48754 | Docs | Datadog PR Page | Give us feedback!

vLLM's unified STORAGE medium maps to the Disk lower tier (ai-dynamo#11571), and
unrecognized media now fail closed instead of misrouting, so the router
does tier-aware routing for vLLM disk offloading. Flip the Disk cell to
✅ and refresh the stale FS/OBJ wording (upstream emits STORAGE; legacy
0.26.0 FS/OBJ media are dropped, not misrouted).

Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator July 30, 2026 23:38 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test d5834a5

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 30, 2026
…rsion

Unrecognized media (vLLM 0.26.0 FS/OBJ) were bypassed to conversion,
which dropped them -- but the LLM ZMQ listener increments next_event_id
before normalize_preprocessed, so a conversion-time drop burned an event
id. The event processor then reads the id gap as a false
engines_dropped_events, and the event is also counted as accepted then
conversion_none.

Classify media in preprocess_with_reason: Device/HostPinned stay on the
normalizer path, Disk/External bypass to conversion (kept there, no id
wasted), and unrecognized media fail closed with a new UnknownMedium
filter reason (label "unknown_medium") -- the same in-preprocess
filtering the ai-dynamo#11571 locality gate uses to avoid this exact id gap.
convert_event keeps its rate-limited defensive drop for direct callers
that bypass preprocess.

Add a preprocess unit test (FS/OBJ/XYZ -> UnknownMedium; recognized
media pass), update the namespace-pollution test to assert the reject,
and add a listener-level GPU -> FS -> GPU test proving the two GPU
stores land on consecutive event ids (the FS store burns none).

Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator July 31, 2026 00:39 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 630b42f

@github-actions github-actions Bot added the router Relates to routing, KV-aware routing, etc. label Jul 31, 2026
@Change72
Change72 marked this pull request as ready for review July 31, 2026 00:49
@Change72
Change72 requested review from a team as code owners July 31, 2026 00:49

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread lib/kv-router/src/zmq_wire/mod.rs
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 314b5353-da34-4fb6-8a63-73412d3756f0

📥 Commits

Reviewing files that changed from the base of the PR and between 8a753c3 and 630b42f.

📒 Files selected for processing (8)
  • docs/fern/components/router/router-offloading.md
  • lib/kv-router/src/protocols.rs
  • lib/kv-router/src/zmq_wire/convert.rs
  • lib/kv-router/src/zmq_wire/mod.rs
  • lib/kv-router/src/zmq_wire/tests.rs
  • lib/kvbm-consolidator/src/ingress/zmq_subscriber.rs
  • lib/llm/src/block_manager/kv_consolidator/subscriber.rs
  • lib/llm/src/kv_router/publisher/tests.rs
💤 Files with no reviewable changes (1)
  • lib/kv-router/src/protocols.rs

Walkthrough

Unknown storage media are now rejected instead of defaulting to the device tier. Router preprocessing, conversion, G1 ingress filters, documentation, and tests were updated to recognize STORAGE while dropping legacy or unknown media.

Changes

Storage media filtering

Layer / File(s) Summary
Tier mapping and preprocessing
lib/kv-router/src/protocols.rs, lib/kv-router/src/zmq_wire/*, docs/fern/components/router/router-offloading.md
Storage media are classified explicitly; absent media map to Device, recognized tiers are converted, and unknown media are dropped with an UnknownMedium reason. vLLM documentation now identifies STORAGE as the supported disk event.
Router filtering regression coverage
lib/kv-router/src/zmq_wire/tests.rs, lib/llm/src/kv_router/publisher/tests.rs
Tests verify unknown media are filtered without namespace mutation or event-ID consumption, while surrounding GPU events retain their hashes and consecutive IDs.
G1 ingress filtering
lib/kvbm-consolidator/src/ingress/zmq_subscriber.rs, lib/llm/src/block_manager/kv_consolidator/subscriber.rs
G1 subscribers now accept only explicit device-tier events and test that unrecognized FS events are not tracked.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description thoroughly explains the motivation, behavior, implementation, scope, and validation, but omits the required Related Issues section and reviewer-start guidance. Add the required Related Issues section, selecting the appropriate issue-linking option, and identify the files or sections where reviewers should start.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: rejecting unrecognized KV-event media instead of defaulting open.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator July 31, 2026 00:54 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 896af5a

…n vLLM 0.27+

Address PR review feedback:

- Empty-string `medium` ("") was routed to the unrecognized-media fail-closed
  path, dropping events a producer may mean as unset. Normalize "" to absent at
  the wire deserialize boundary (mirroring the empty `cache_salt` handling) so an
  unset medium stays on the default device tier and is indexed. No dynamo/vLLM
  producer emits `medium: ""` today; this preserves prior behavior for that
  value and keeps fail-closed scoped to populated-but-unrecognized media.

- Router offloading matrix: the vLLM Disk ✅ depends on the unified STORAGE
  medium (vLLM 0.27+). Qualify the cell with the 0.27+ gate so readers on the
  pinned 0.26.0 (which emits FS/OBJ that Dynamo drops) are not misled.

Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator July 31, 2026 01:09 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test cd14d9f

@Change72
Change72 temporarily deployed to external_collaborator July 31, 2026 01:32 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 833fe06

Signed-off-by: Chang Guo <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator August 10, 2026 21:53 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 5f352d7

@jthomson04 jthomson04 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.

Two source review findings.

Comment thread lib/kv-router/src/zmq_wire/mod.rs
Comment thread lib/kv-router/src/zmq_wire/deserialize.rs
…nce path

normalize_medium applies to both the map and positional (tuple) decode
branches, but the regression test only exercised map-encoded events.
Add a sequence-encoded store + remove case asserting an empty-string
medium decodes as absent and stays on the device tier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 temporarily deployed to external_collaborator August 10, 2026 22:45 — with GitHub Actions Inactive
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 7e48754

@Change72
Change72 enabled auto-merge (squash) August 10, 2026 23:53
@Change72
Change72 merged commit 5b11243 into ai-dynamo:main Aug 10, 2026
112 checks passed
@Change72
Change72 deleted the codex/unrecognized-media-fail-closed branch August 10, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation external-contribution Pull request is from an external contributor fix router Relates to routing, KV-aware routing, etc. size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants