Skip to content

feat(router): add header-based session affinity [DYN-3249] - #10875

Merged
PeaBrane merged 20 commits into
mainfrom
codex/header-session-affinity
Jun 24, 2026
Merged

PeaBrane merged 20 commits into
mainfrom
codex/header-session-affinity

Conversation

@PeaBrane

@PeaBrane PeaBrane commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add X-Dynamo-Session-ID with native Claude Code, Codex, and OpenCode header fallbacks while keeping trajectory identity independent.
  • Add router-local atomic first-dispatch affinity with idle expiry, active-request leases, exact no-fallback routing, strict Direct validation, and phase-local prefill/decode bindings across routing modes.
  • Add the independent --router-session-affinity-ttl-secs configuration and document migration from the removed body-level session-control API.

This PR sends no backend lifecycle RPCs and does not couple session affinity to trajectory identity or SGLang radix tagging. It supersedes #10854, which implemented the previous explicit lifecycle-based design.

Validation

  • cargo fmt --all --check
  • cargo clippy -p dynamo-llm --no-default-features -- -D warnings
  • cargo clippy --manifest-path lib/bindings/python/Cargo.toml --no-default-features -- -D warnings
  • cargo test -p dynamo-llm --lib --no-default-features session_affinity
  • cargo test -p dynamo-llm --lib --no-default-features kv_router::push_router
  • cargo test -p dynamo-llm --lib --no-default-features kv_router::prefill_router
  • .venv/bin/python -m pytest components/src/dynamo/common/tests/configuration/test_kv_router_args.py -q
  • cargo metadata --locked --no-deps --format-version 1 in the root workspace and Python binding workspace
  • fern check
  • fern docs broken-links
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Added configurable session affinity timeout via --router-session-affinity-ttl-secs (default: 300 seconds) for routing session-specific requests to bound workers.
  • Documentation

    • Clarified session affinity headers (X-Dynamo-Session-ID) and trajectory identity headers (X-Dynamo-Trajectory-ID) as independent mechanisms for routing and request tracing.
    • Updated agent harness documentation to specify how different agent systems map session and trajectory headers to routing behavior.

PeaBrane added 3 commits June 23, 2026 09:44
Signed-off-by: PeaBrane <yanrpei@gmail.com>
Signed-off-by: PeaBrane <yanrpei@gmail.com>
Signed-off-by: PeaBrane <yanrpei@gmail.com>
@github-actions github-actions Bot added feat documentation Improvements or additions to documentation frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` router Relates to routing, KV-aware routing, etc. labels Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

@datadog-official

This comment has been minimized.

Signed-off-by: PeaBrane <yanrpei@gmail.com>
Signed-off-by: PeaBrane <yanrpei@gmail.com>
@PeaBrane
PeaBrane marked this pull request as ready for review June 23, 2026 17:42
@PeaBrane
PeaBrane requested review from a team as code owners June 23, 2026 17:42
@PeaBrane
PeaBrane requested a review from a team June 23, 2026 17:42
Signed-off-by: PeaBrane <yanrpei@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR implements session affinity routing for Dynamo: HTTP request headers (X-Dynamo-Session-ID and harness-specific equivalents) are parsed into a SessionAffinityId and stored in request context. A new AffinityCoordinator maps session IDs to worker bindings with TTL-based expiry. SessionAffinityPushRouter and KvPushRouter use the coordinator to pin sessions to workers. The session_affinity_ttl_secs parameter is plumbed through RouterConfig, PrefillRouter, build_preprocessed_routing, Python/C bindings, and a new CLI flag. Trajectory identity headers are decoupled from session affinity headers.

Changes

Session Affinity Routing

Layer / File(s) Summary
Runtime registry optional-lookup helpers
lib/runtime/src/pipeline/registry.rs, lib/runtime/src/pipeline/context.rs
Registry::get_shared_optional now returns Ok(None) for absent keys; Context::get_optional delegates to it. Tests added for all three result states.
AffinityCoordinator state machine, TTL reaper, and module exports
lib/llm/src/session_affinity/coordinator.rs, lib/llm/src/session_affinity/mod.rs, lib/llm/src/lib.rs, lib/llm/Cargo.toml
Implements AffinityTarget, initializing/bound AffinityEntry with revision-based synchronization, background reaper, acquire/query_target, AffinityInitialization commit/drop, and AffinityLease/AffinityTrackedStream lifecycle. Module exports, MAX_SESSION_AFFINITY_TTL_SECS, and LlmResponse alias declared in mod.rs.
AffinityCoordinator and SessionAffinityPushRouter unit tests
lib/llm/src/session_affinity/tests.rs, lib/llm/src/session_affinity/push_router.rs (test block)
Paused-time tokio tests cover initialization atomicity, initializer cancellation, worker/rank contract, lease prevents expiry, stream drop/EOF refreshes TTL, failed bound does not refresh TTL, query_target is read-only, reaper lifecycle, TTL validation, rollback on failed dispatch, and rebinding.
SessionAffinityPushRouter prefill and generate dispatch
lib/llm/src/session_affinity/push_router.rs
Wraps PushRouter with AffinityCoordinator; implements session-aware worker selection for both prefill and AsyncEngine::generate, with query-only, acquire, and affinity operation stream wiring paths.
Agent header constants and SessionAffinityId protocol types
lib/llm/src/protocols/agents.rs, lib/llm/src/protocols/common/extensions.rs
Rewrites agent_context_header_values to derive trajectory identity from Dynamo/Claude headers only; adds session_affinity_header_value scanning provider session headers. Defines SESSION_AFFINITY_CONTEXT_KEY, SessionAffinityId, and session_affinity_from_headers. Tests verify header precedence and independence from agent context.
HTTP service session affinity header propagation
lib/llm/src/http/service/openai.rs, lib/llm/src/http/service/anthropic.rs
Both OpenAI and Anthropic handlers extract session affinity from request headers and insert it into the per-request Context under SESSION_AFFINITY_CONTEXT_KEY; copy_context_metadata preserves it across context copies.
RouterConfig session_affinity_ttl_secs field and builder
lib/llm/src/entrypoint.rs
Adds DEFAULT_SESSION_AFFINITY_TTL_SECS constant, session_affinity_ttl_secs field with serde default, explicit impl Default, initialization in ::new, and with_session_affinity_ttl_secs builder.
PrefillRouter session affinity integration
lib/llm/src/kv_router/prefill_router/activation.rs, .../admission.rs, .../mod.rs
PrefillRouter::disabled/::new gain session_affinity_ttl_secs; activation wraps simple routing in SessionAffinityPushRouter and passes TTL to KvPushRouter::new; InnerPrefillRouter::SimpleRouter retyped; select_and_dispatch_prefill removes pinned_worker parameter; mod.rs extracts and injects SessionAffinityId into prefill context.
KvPushRouter and worker selection affinity integration
lib/llm/src/kv_router/push_router.rs, lib/llm/src/kv_router/push_router/selection.rs
KvPushRouter stores AffinityCoordinator; constructor returns Result; select_with_affinity, track_selection(is_query_only), reworked select_and_dispatch_prefill, and generate use affinity operation for dispatch/stream wrapping. select_worker gains affinity_worker parameter; merge_affinity_pin helper merges explicit and affinity pins.
build_preprocessed_routing and discovery wiring
lib/llm/src/entrypoint/input/common.rs, lib/llm/src/discovery/watcher.rs, .../model.rs, .../model_manager.rs
build_preprocessed_routing gains session_affinity_ttl_secs; threads it into PrefillRouter::disabled and preprocessed_backend_engine. watcher.rs forwards from router_config. Discovery test helpers pass DEFAULT_SESSION_AFFINITY_TTL_SECS.
Python and C bindings for session_affinity_ttl_secs
lib/bindings/python/rust/llm/entrypoint.rs, lib/bindings/python/rust/llm/kv.rs, lib/bindings/c/src/lib.rs
Python RouterConfig and KvRouter bindings gain session_affinity_ttl_secs with default 300 and 1..=31_536_000 validation; From<RouterConfig> updated; C binding passes DEFAULT_SESSION_AFFINITY_TTL_SECS to PrefillRouter::new.
Python component config: CLI/ENV, validation, and tests
components/src/dynamo/common/configuration/groups/router_args.py, components/src/dynamo/frontend/frontend_args.py, components/src/dynamo/common/tests/configuration/test_kv_router_args.py
Adds --router-session-affinity-ttl-secs / DYN_ROUTER_SESSION_AFFINITY_TTL_SECS CLI arg (default 300); FrontendConfig.validate() enforces 1..31_536_000 range. Tests add env/CLI precedence and out-of-range rejection coverage.
Session affinity and trajectory identity documentation
docs/components/router/router-configuration.md, docs/components/router/router-filtering.md, docs/components/frontend/nvext.md, docs/agents/..., docs/backends/sglang/agents.md
New Session Affinity section in router-configuration.md; agent harness docs (README, harnesses, trajectory-ids, sglang) updated to separate session affinity headers from trajectory identity headers across all supported harnesses.
Frontend API surface compliance test adjustments
tests/frontend/test_frontend_api_surface_compliance.py
Removes parent-trajectory-specific trace assertions from Codex/Claude/OpenCode smoke flows; simplifies _trace_contains_claude_subagent_context; broadens OpenCode trace detection to any request-trace record.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides a clear summary of changes, validation details, and context about superseding a previous PR, but lacks the explicitly required sections from the template (Overview, Details, Where should the reviewer start, and Related Issues). Add the missing template sections: expand Overview and Details sections, specify reviewer focus areas, and explicitly state the related issue status (whether linked or not confirmed).
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: adding header-based session affinity to the router, which is the central change across the entire changeset.
Docstring Coverage ✅ Passed Docstring coverage is 81.54% 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch codex/header-session-affinity

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

coderabbitai[bot]

This comment was marked as resolved.

Signed-off-by: PeaBrane <yanrpei@gmail.com>
Signed-off-by: PeaBrane <yanrpei@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>

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

a

Signed-off-by: PeaBrane <yanrpei@gmail.com>
@PeaBrane
PeaBrane temporarily deployed to external_collaborator June 24, 2026 19:46 — with GitHub Actions Inactive
@PeaBrane
PeaBrane enabled auto-merge (squash) June 24, 2026 19:47
Signed-off-by: PeaBrane <yanrpei@gmail.com>
@ishandhanani
ishandhanani temporarily deployed to external_collaborator June 24, 2026 21:44 — with GitHub Actions Inactive
@PeaBrane
PeaBrane merged commit 9e38cbe into main Jun 24, 2026
100 checks passed
@PeaBrane
PeaBrane deleted the codex/header-session-affinity branch June 24, 2026 22:29
@PeaBrane PeaBrane changed the title feat(router): add header-based session affinity feat(router): add header-based session affinity [DYN-3249] Jun 25, 2026

This branch had an error being deployed

1 failed and 1 inactive deployments
GITLAB — 685e7232 Deployed Jun 24, 2026 by copy-pr-bot[bot] via Trigger CI Pipeline #31951
external_collaborator — 685e7232 Deployed Jun 24, 2026 by ishandhanani via ok-to-test #202
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 feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` router Relates to routing, KV-aware routing, etc. size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants