NVIDIA / NVIDIA/nvcf

feat(stargate): expose control API for config and routing state

Open
#1,638 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
218
Forks
72
Avg merge
1d 12h
Merged PRs (30d)
427

Description

Stargate debug APIs plan

Goal

Expose the selected Stargate pod's configuration and routing snapshots through
the same load balancer and listener that serve ListModels.

Add these routes:

  • GET /debug/config
  • GET /debug/routing_state/
  • GET /debug/routing_state/{model_id}
  • GET /debug/routing_state/{model_id}?routing_key={routing_key}

Every response includes the selected pod's stargate_id. The response is local
to that pod. It is not an aggregate of all Stargate replicas.

Design

Configuration

Keep one owned DebugConfig value in the model-discovery server state. Build it
once from the same effective values used to start Stargate. DebugConfig does
not contain stargate_id; the response envelope is the only source of pod
identity.

GET /debug/config clones that value and returns:

{
  "stargate_id": "stargate-pod-name",
  "config": { ... }
}

Include all effective configuration that is useful for debugging. Omit secret
values, private key contents, certificate contents, and authentication tokens.
The response has these direct groups:

  • listeners: actual bound and advertised addresses
  • discovery: mode, peers, DNS settings, and polling or heartbeat intervals
  • lifecycle: readiness, drain, and registration timeouts
  • transport: connectivity mode, tunnel protocol, connection counts, TLS mode,
    and transport timeouts
  • proxy: retry counts and replay settings
  • load_balancer: the effective default and per-model policy loaded at startup
  • telemetry: endpoint, service name, enabled state, and whether auth is
    configured
  • worker_auth: endpoint, mode, enabled state, and whether credentials are
    configured

Represent durations as integer milliseconds and enum values as stable strings.
Represent secret-backed settings with configured booleans, never secret values
or secret file contents.

Capture normalized arguments before startup consumes them, actual addresses
after binding, and the parsed load-balancer policy from the same value used to
construct routing. Assemble one DebugConfig after those values exist. Do not
add a separate configuration framework or runtime reload path.

Routing state

Add one routing-state method:

debug_snapshot() -> HashMap<RoutingTargetKey, DebugTargetSnapshot>

The method directly walks the existing routing target map and clones its
current contents into owned, serializable debug values:

  • Clone each RoutingTargetKey and target handle from the top-level map.
  • For each target, hold its existing generation lock while walking its current
    cluster map.
  • For each cluster, call one new cluster debug_snapshot helper that holds the
    existing cluster generation lock once and clones both the effective aggregate
    and every current backend.
  • Release all routing locks before returning from debug_snapshot.

DebugTargetSnapshot contains only cluster data. RoutingTargetKey remains the
single source of model_id and routing_key. The owned view contains:

  • clusters
  • cluster stats, lowercase status, rtt_ms, snapshot_age_ms, and active backend
    count
  • every backend's ID, URL, full ModelStats, lowercase status, rtt_ms,
    snapshot_age_ms, and tunnel direction

Capture one Instant::now() at the start of the top-level clone and use it for
every snapshot_age_ms value. Use saturating elapsed conversion.

The full handler calls debug_snapshot and returns every map entry:

{
  "stargate_id": "stargate-pod-name",
  "routing_targets": [ ... ]
}

The model handler calls the same debug_snapshot method, then retains entries
whose RoutingTargetKey.model_id matches the path parameter. If routing_key is
present, it also retains entries with that exact routing key. An empty
routing_key selects the unscoped target. After filtering, move identity from
each map key into the response item. Sort targets by model_id and routing_key,
clusters by cluster ID, and backends by backend ID.

This is an eventually consistent debug view. It does not retry, revalidate
generations, create a cross-target transaction, or claim an atomic observation
of the whole process. A target's cluster map is cloned under the target lock.
Each cluster aggregate and backend list are cloned under one nested cluster
lock, using the existing target-then-cluster lock order.

Implement the model route with Axum's catch-all path capture so model IDs
containing slash remain one value. Use Axum's decoded Path value directly; do
not decode it a second time. The empty model ID remains visible in the full
view but has no model-route spelling because /debug/routing_state/ is the
collection route. Do not add custom identifier encoding or a load-balancer
fallback.

Serving

Build tonic::service::Routes from StargateModelDiscoveryServer, convert it with
into_axum_router(), add the debug routes to that router, and serve the existing
model-discovery TcpListener with axum::serve and the existing shutdown signal.
ListModels must continue to work on the same listener.

Expose ordinary HTTP GET traffic through the existing load balancer that
already serves ListModels. Update that existing resource only. Do not create a
second load balancer or expose the routes on the inference proxy or backend
control-plane listeners.

Use the access control already applied to the ListModels load balancer. This
change does not add an application bearer-token system or a new NetworkPolicy.

Remove the old /debug/state route from the inference HTTP proxy.

Implementation

0. Identify the existing ListModels load balancer

Before code changes, record the exact resource, owner, listener, backend port,
and access control that currently expose ListModels. This is a blocking check,
not a new design project.

The implementation is complete only when that resource routes ListModels and
the debug HTTP requests to the same model-discovery listener. If it is owned
outside this repository, the linked deployment change is part of completion,
not a follow-up note.

1. Clone the routing snapshots

Update the routing-state module under:

  • src/libraries/rust/stargate/crates/stargate/src/routing_state/

Add the small owned debug snapshot structs and three direct methods:

  • RoutingLifecycle::debug_snapshot clones the top-level target map.
  • RoutingTargetState::debug_snapshot holds the target lock while cloning its
    current cluster map.
  • RoutedClusterState::debug_snapshot holds the cluster lock once while cloning
    the effective aggregate and backend values.

The public state method returns the one owned
HashMap<RoutingTargetKey, DebugTargetSnapshot>. These are implementation-local
helpers for the three existing lock levels, not a new routing abstraction.

Do not change routing updates, registration ownership, reservations, load
balancing, or snapshot lifecycle.

2. Capture the full non-secret configuration

Expand the existing DebugConfig into the groups listed in the API contract.
Construct it once from the effective startup values. Keep stargate_id in the
server state and response envelope, not inside DebugConfig.

3. Add the HTTP handlers

Move the existing debug configuration support out of the inference proxy and
place it with the new debug handlers.

The handlers:

  • clone DebugConfig or call debug_snapshot
  • apply the optional model and routing-key filter
  • convert the retained map keys and values to sorted response arrays
  • attach stargate_id
  • return Axum Json

No pagination, streaming, capped writer, semaphore, retry loop, custom error
framework, or new debug-only metric families are part of this change. Apply the
existing request logging, tracing, and RED telemetry to the fixed debug route
names.

4. Serve HTTP beside ListModels

Update runtime/server_tasks.rs with the Tonic Routes to Axum construction
described above.

Keep the existing listener address, task ownership, graceful shutdown, and
pod-local state.

5. Update the existing ListModels load balancer

Update the resource identified in step 0 to allow HTTP GET requests to the two
debug resources on that same backend and port.

Do not create another Service.

Tests

Add focused tests only:

  • debug_snapshot clones every current target, cluster, backend, and ModelStats
    value from a representative routing map.
  • One cluster debug_snapshot call returns a matching effective aggregate and
    backend list.
  • A target clone returns all clusters from one captured target map.
  • GET /debug/routing_state/ returns the complete cloned map and stargate_id.
  • The model route returns all routing keys for only that model.
  • The routing_key query returns the exact model and routing-key pair.
  • Slash-bearing model IDs work through Axum and the real load balancer. Literal
    percent sequences are decoded exactly once.
  • Exact JSON tests cover rtt_ms, snapshot_age_ms, lowercase status, full
    ModelStats, and deterministic ordering.
  • GET /debug/config returns every documented effective DebugConfig group and
    top-level stargate_id without secret values or duplicate pod identity.
  • ListModels still works over gRPC on the combined listener.
  • Unknown gRPC services keep Tonic's unimplemented fallback, and graceful
    shutdown still stops the combined listener.
  • The existing ListModels load balancer passes both ListModels and the debug
    HTTP requests.
  • With two Stargate replicas, the returned stargate_id identifies which pod's
    local snapshot was returned.
  • The inference proxy no longer serves /debug/state or the new debug routes.

Do not duplicate routing lifecycle, heartbeat, reservation, or concurrency
tests that already cover how the snapshot map is maintained.

Verification

Run:

cargo fmt --manifest-path src/libraries/rust/stargate/Cargo.toml --all --check
cargo test --manifest-path src/libraries/rust/stargate/Cargo.toml -p stargate
bazel test //src/libraries/rust/stargate/crates/stargate:stargate_test --test_output=streamed
git diff --check

Run the existing deployment render tests for the resource that owns the
ListModels load balancer.

Acceptance criteria

  • The existing ListModels load balancer exposes /debug/config and
    /debug/routing_state/.
  • The required model route and optional routing-key filter work.
  • Every response includes stargate_id.
  • The routing response is a direct owned clone of the selected pod's current
    routing snapshot map, including all cluster and backend stats. Target identity
    comes only from RoutingTargetKey.
  • Each target map is cloned under its target lock, and each cluster aggregate
    and backend list are cloned under one cluster lock.
  • The configuration response returns the documented effective non-secret config
    with no duplicate stargate_id.
  • The actual existing ListModels load balancer serves both gRPC ListModels and
    debug HTTP requests.
  • ListModels and existing routing behavior remain unchanged.

Non-goals

  • Aggregating state across Stargate pods.
  • Strong or atomic consistency across routing targets.
  • Pagination, streaming, response limits, or debug-specific load shedding.
  • A new authentication, authorization, secret-rotation, or NetworkPolicy
    subsystem.
  • New routing abstractions or changes to the routing data model.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by identifying the existing ListModels load balancer resource, then read src/libraries/rust/stargate/crates/stargate/src/routing_state/ and runtime/server_tasks.rs. Run the listed Rust formatting and test commands before changing routing snapshots, debug handlers, and the combined listener. Done means the existing load balancer serves the debug routes and ListModels while the focused tests cover cloning, filtering, ordering, identity, and shutdown.

Written by the indexing model from the issue text.

Assessment

Tech stack
kubernetes, rust
Domain
api, backend, devops
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.