Skip to content

feat(operator): allow EPP replicas in Dynamo mode - #14758

Merged
atchernych merged 20 commits into
mainfrom
feat/epp-replicas-dynamo-mode
Sep 29, 2026
Merged

atchernych merged 20 commits into
mainfrom
feat/epp-replicas-dynamo-mode

Conversation

@atchernych

@atchernych atchernych commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Note

Targets a later release, not 1.5.0. #14756 documents the single-replica limit as it stands in 1.5.0 and should land on its own; this PR changes that behaviour for a subsequent release.

Summary

The EPP in the standalone mode allows replication but the EPP in the dynamo mode only allowed replica 1. This PR addresses this limitation.

Prior to this PR Admission was rejected any EPP component with replicas other than 1, so the endpoint picker could neither scale for throughput nor survive the loss of its pod. That restriction was not justified by a router limitation. The Frontend hosts the same KV router and allows for replication:

This PR does not introduce any new impresisions. It extends the current frontEnd behavior to the EPP in the dynamo mode.

  • Prefix-cache state needs no synchronization. Workers publish KV events to the event plane and every subscriber receives all of them (plain NATS subscribe, not queue_subscribe), so each replica builds an equivalent radix tree. Cache-overlap scoring, which dominates worker choice on prefix-heavy traffic, stays as accurate with N replicas as with one.
  • Active-sequence state is shared by DYN_ROUTER_REPLICA_SYNC, IF ENABLED just like in the frontEnd, which reaches the EPP's decode and prefill routers through try_kv_router_config_from_dynamo_env and runs the same ActiveSequencesMultiWorker replica sync the Frontend uses.
  • The load count will also be off just like with the FrontEnd replication.
  • The freeing of a Booking is also supported in EPP.

Changes

  • Remove the EPP component must have exactly 1 replica admission rule.
  • Leave DYN_ROUTER_REPLICA_SYNC off by default, matching the Frontend-hosted router.

Validation

  • go test ./... in deploy/operator — 42 packages pass, no failures.
  • Admission tests cover both paths: a native Rust EPP accepts more than one replica, and a legacy Go EPP still requires exactly one.
  • Updated the envtest case that asserted rejection of replicas: 2 to assert acceptance, and the field-path test that pinned spec.components[].replicas among the EPP validator's emitted paths.
  • python3 docs/fern/scripts/docs_lint.py --scan docs — 0 errors (8 pre-existing warnings on untouched files).
  • fern check / fern docs broken-links not run: the CLI is not installed locally.

The magnitude of the selection-to-booking race at realistic arrival rates needs measurement.

Related Issues

🚫 This PR is NOT linked to an issue:

  • Confirmed — no related issue

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • EPP deployments can now run with multiple replicas instead of being limited to a single replica.
    • Native Rust EPP deployments automatically enable replica synchronization when multiple replicas are configured, while allowing explicit configuration overrides.
    • Replica settings are now applied consistently across deployments.
  • Documentation

    • Expanded EPP guidance covering operating modes, dependencies, discovery, permissions, replication, synchronization, and routing accuracy.

@atchernych
atchernych requested review from a team as code owners September 11, 2026 23:48
@github-actions github-actions Bot added feat documentation Improvements or additions to documentation deployment::k8s Relates to dynamo deployment in kubernetes labels Sep 11, 2026
@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

atchernych and others added 2 commits September 11, 2026 17:33
The Gateway API routing reference described operator-generated EPP
resources without stating which EPP mode they apply to, and documented
spec.components[].replicas as something to increase for more endpoint-picker
capacity. Admission rejects any value other than 1, and EPP replication is
reachable only in standalone mode, where DYN_EPP_PEER_SERVICE enables
peer discovery and cross-replica active-load sync.

Readers could therefore take the operator chart's EPP RBAC as the EPP's
RBAC in general and bind a standalone EPP to it. That ClusterRole covers
only what a Dynamo-mode EPP reads; a standalone deployment ships its own
ServiceAccount and Role.

Add an EPP Modes section contrasting the two modes and stating that
standalone mode runs no operator, correct the replicas field to the value
admission enforces, and note that DYN_ROUTER_REPLICA_SYNC has no peer
under the single-replica cap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Admission rejected any EPP component with replicas other than 1, so the
endpoint picker could neither scale for throughput nor survive the loss of
its pod. The restriction was not justified by a router limitation: the
Frontend hosts the same KV router, is replicated routinely, and carries no
such rule. It was the only per-component replica restriction in the
validator.

The machinery a replicated EPP needs is already in place. Prefix-cache
state needs no synchronization, because workers broadcast KV events over
the event plane and every subscriber receives all of them, so each replica
builds an equivalent radix tree. Active-sequence state is shared by
DYN_ROUTER_REPLICA_SYNC, which reaches the EPP's routers through
try_kv_router_config_from_dynamo_env and runs the same
ActiveSequencesMultiWorker replica sync the Frontend uses.

Remove the admission rule and default DYN_ROUTER_REPLICA_SYNC to true when
replicas exceeds 1, so a scale-out does not silently leave each replica
scoring workers from only the load it placed itself. podTemplate env keeps
precedence, so the isolated-view mode the Frontend also offers stays
reachable. The legacy Go EPP never receives the setting.

Document the accuracy cost in the Gateway API routing reference, including
the EPP-specific limit the Frontend does not have: the EPP only selects
while Envoy dispatches, so a booking lives for the ext_proc stream rather
than the backend request. A retry that lands on another replica frees the
original booking while work continues and books again elsewhere, and
expiry bounds the error without correcting it.

Also adjusts the replication statements added in the preceding commit,
which described the single-replica cap this change removes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych force-pushed the feat/epp-replicas-dynamo-mode branch from c3ae313 to 8d02695 Compare September 12, 2026 00:34
@atchernych
atchernych requested review from a team as code owners September 12, 2026 00:34
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@pull-request-size pull-request-size Bot added size/M and removed size/L labels Sep 23, 2026
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych

Copy link
Copy Markdown
Contributor Author

Same-node rerun

Follow-up to the perf run above. Today I pinned both deployments' prefill and decode workers to the same B200 node and ran the same sweep again, alternating main, branch, main, branch. The images were the same as yesterday's.

TTFT p50, concurrency 1 Yesterday (different nodes) Today (same node)
main 588 ms 461 ms
branch 479 ms 566 ms

main got faster once its workers ran on the same node, so yesterday's gap came from worker placement. It is unlikely that the EPP change in this PR is the culprit.

On the same node the branch was the slower side this time, but the extra time is again already present at the decode pod's sidecar, before the Gateway and EPP. The Gateway + EPP hop costs about 10 ms on both sides. The remaining difference comes from which GPUs each side's workers landed on and what else runs on that node, not from this PR.

Comment thread docs/fern/pages/reference/components/gateway-api-routing.mdx Outdated
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych enabled auto-merge (squash) September 23, 2026 21:40
@atchernych
atchernych removed request for a team September 23, 2026 21:42

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

Docs review. No blockers. The doc matches the code (Go EPP detection via eppConfig, sync off by default, ZMQ as the default event plane), and the links and anchors resolve. A few wording and content fixes are inline.

Comment thread docs/fern/pages/reference/components/gateway-api-routing.mdx Outdated
Comment thread docs/fern/pages/reference/components/gateway-api-routing.mdx
Comment thread docs/fern/pages/reference/components/gateway-api-routing.mdx
atchernych and others added 2 commits September 28, 2026 08:54
Co-authored-by: Harry Kim <harry_kim@live.com>
Signed-off-by: atchernych <atchernych@nvidia.com>
Trim the DYN_ROUTER_REPLICA_SYNC description and list, in the EPP
replication section, which router state is shared across replicas,
which is best-effort, and which stays per replica. Mechanism details
are linked from router-operations.md rather than repeated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>

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

approved

@atchernych
atchernych merged commit 0ce693b into main Sep 29, 2026
115 checks passed
@atchernych
atchernych deleted the feat/epp-replicas-dynamo-mode branch September 29, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deployment::k8s Relates to dynamo deployment in kubernetes documentation Improvements or additions to documentation feat size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants