Skip to content

New session_id_header for session affinity scorer plugin - #2238

Open
D-Sai-Venkatesh wants to merge 13 commits into
llm-d:mainfrom
praveingk:feat/session-affinity-session-id-header
Open

New session_id_header for session affinity scorer plugin#2238
D-Sai-Venkatesh wants to merge 13 commits into
llm-d:mainfrom
praveingk:feat/session-affinity-session-id-header

Conversation

@D-Sai-Venkatesh

@D-Sai-Venkatesh D-Sai-Venkatesh commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind feature

What this PR does / why we need it:
Adds a session_id_header algorithm to the session-affinity-scorer and session-affinity-filter, picked with a new strategy param next to the existing default encoded_endpoint_header. Both algorithms now sit behind a small internal strategy interface (score/preRequest/responseHeader), so the plugin just delegates.

The session ID comes from a request header, or from the attribute set by the agent-identity plugin. The scorer remembers which pod served that session and gives highest score for the same, dropping the mapping after a TTL if the session goes idle.

  • New session lands on the candidate pod with the fewest bound sessions.
  • Bound session stays on its pod while that pod is still a candidate.
  • Bound session whose pod dropped out of the candidate set migrates to the present pod with the fewest bound sessions, on the next request.
  • Bindings unused past the TTL are swept in the background.

Which issue(s) this PR fixes:

Fixes #2019

Release note (write NONE if no user-facing change):

Adds a new `session_id_header` session affinity strategy that pins a client-supplied session id (from a header or request attribute) to a pod, alongside the existing `encoded_endpoint_header`. Session affinity plugins (`session-affinity-scorer`, `session-affinity-filter`) now take per-strategy config blocks: `encodedEndpointHeaderConfig` (with `header`) and `sessionIdConfig` (with `sources` and eviction settings).

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
…terface

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
…header

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
…nity

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
…header tests

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
@D-Sai-Venkatesh
D-Sai-Venkatesh requested a review from a team as a code owner July 30, 2026 13:53
@github-actions github-actions Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jul 30, 2026
@D-Sai-Venkatesh

Copy link
Copy Markdown
Contributor Author

Here is an initial result from our early evaluation:
image

Setup:

  • Model: nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8
  • Decode deployment: 3 replicas (decode only), TP 4, H100s
  • Traces: Exgentic/agent-llm-traces
  • Static EPP scorers: queue-scorer (weight: 2), kv-cache-utilization-scorer (weight: 2), prefix-cache-scorer(weight: 3, only in precise-prefix scenario), session-affinity-scorer (weight: 3, only in session-affinity scenario)

@liu-cong liu-cong self-assigned this Jul 30, 2026
@D-Sai-Venkatesh

Copy link
Copy Markdown
Contributor Author

Will push one more commit which will move the session_id_header algorithm to util folder and it will be consumed by both session affinity scorer and filter.

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
@github-actions github-actions Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 31, 2026
@D-Sai-Venkatesh

Copy link
Copy Markdown
Contributor Author

Hey @liu-cong, just pushed a new commit to move the core session_id_header algorithm into a shared type in util/sessionaffinity so the scorer and filter both use it instead of each keeping a copy. Also added session_id_header to the filter. Let me know if any changes are required.

)

// parameters configures the SessionAffinity filter.
type parameters struct {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest the following refactor.

  1. Separate config struct for different strategy instead of a flat structure. This is clean and easy to extend.
  2. For the SessionIDStrategy, we should not by default look at the agent identity attribute. Session affinity plugins should be a self contained plugin and should not be tied to other plugins. We can let the user configure it explicitly.
  3. I propose we support multiple "session id sources" in priority order. This is very flexible - it allows the user to specify a header, and fallback to the agent id attribute for agent traffic.
type parameters struct {
	// Strategy is StrategyEncodedEndpointHeader (the default) or
	// StrategySessionID.
	Strategy string `json:"strategy"`
	// Only one of the strategies should be configured
    EncordedEndpointHeaderConfig EncordedEndpointHeaderConfig
    SessionIDConfig SessionIDConfig
}

// Shared between filter and corer, in util/sessionaffinity
type EncordedEndpointHeaderConfig struct {
    // Name of the http header
    // defaults to `x-session-token`
  	Header string `json:"header"`
}

type SessionIDConfig struct {
    // If no sources are configured, defaults to http header `x-session-id`
    // If multiple sources are configured, they are evaluated in priority order
    Sources []SessionIDSource `json:"sources"`
   	// EvictionTTLSeconds is how long a session binding survives unused.
	// session_id_header only.
	EvictionTTLSeconds float64 `json:"evictionTtlSeconds"`
	// EvictionSweepSeconds is how often expired bindings are swept.
	// session_id_header only.
	EvictionSweepSeconds float64 `json:"evictionSweepSeconds"`
}

type SessionIDSource struct {
   // Only one of the following sources can be configured.
   // Name of the http header
   Header string `json:"header"`
   // Name of the request attribute populated by upstream plugins
   Attribute string `json:"attribute"`
}

@D-Sai-Venkatesh D-Sai-Venkatesh Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, implemented the above (per-strategy config, ordered sources, no implicit agent-identity fallback). The config types and resolution logic live in util/sessionaffinity for consumption by both the scorer and filter.

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
…orer

Signed-off-by: Dasari Surya Sai Venkatesh <suryasai.venkatesh@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Evolving session affinity

2 participants