Skip to content

feat(global-router): read pool models from registrations, not a file - #17332

Merged
atchernych merged 82 commits into
mainfrom
atchernych/discovery-pool-registrations
Nov 3, 2026
Merged

atchernych merged 82 commits into
mainfrom
atchernych/discovery-pool-registrations

Conversation

@atchernych

@atchernych atchernych commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Before this PR the POST /pools answered with HTTP 501 "this service takes no registrations". This was ok for the original K8 design but the problem is that we also need to communicate the model from the pools to the Global Router.

Note that model info cannot be communicated to the Global Discovery service through our custom mcs implementation either. The controller copies a Service's dynamo.nvidia.com/mcs-* annotations to the other clusters, so a model annotation could ride along. But the annotations land on the ServiceImport, not on the EndpointSlices the discovery service reads. This would be a less robust approach because: the service would need a second watch; someone has to write the annotations manually. The PoolRelay already knows it.

This PR is an alternative to #17276. Both make MCS pools routable by giving each pool a model and live frontend addresses. #17276 reads the model from a hand-written --routing-config file. This PR gets it from the PoolRelay itself, through POST /pools, as the design says.

Stacked on #17371.

Part of #17600, the pool discovery DEP.

How it works

  • With MCS pool discovery (--pool-discovery karmada or mcs), POST /pools now works (it returned 501). Each call is the pool's heartbeat, as with --pool-discovery registrations.
  • So every --pool-discovery value takes registrations: the lease flags and --registration-storage apply to all of them, while the frontend flags work only with karmada or mcs.
  • A pool is listed only when both sides agree: MCS shows a Ready PoolRelay, and the pool registered within the lease (15 s by default).
  • MCS gives the addresses: the Relay's, and the frontends' from the <dgd>-frontend Service, the same way as feat(global-router): resolve MCS pool models and live frontends #17276. Each frontend pod is listed once, at its IPv4 address on a dual-stack cluster. The registration gives the model, capabilities, capacity and location.
  • One discovery instance keeps the registrations it receives, so run one, and have every PoolRelay register with it. Sharing registrations between the instances in every cluster (--peer-service) moved to feat(global-router): share registrations between discovery instances #17637, stacked on this PR, since shared storage such as DynamoDB may replace it.

Problems with the file in #17276

  1. The model is typed twice: in the pool's deployment and in the file. If they differ, requests go to a pool that doesn't serve that model.
  2. The same file goes to every cluster. Adding a pool or changing a model means editing it, applying it everywhere, and restarting every discovery service.
  3. MCS pools get no capacity or capabilities. So every MCS pool in feat(global-router): resolve MCS pool models and live frontends #17276 shows "capabilities": [] and "capacity": []. In this PR, both come from the PoolRelay's registration. Today's round-robin router doesn't use either field yet. They matter for later work, such as load-aware routing (how big is each pool) or sending requests only to pools that can handle them.
  4. In MCS mode, POST /pools still returns 501, but the design has PoolRelays register in every mode. A PoolRelay pointed at an MCS-mode service would get an error on every heartbeat.
  5. A pool is live only by MCS readiness. If a cluster loses contact with the MCS implementation, its pools can stay Ready after they are gone, until routers notice that the PoolRelay's connection is gone (see the comment above ready_addresses in hub.rs).

What this PR adds

  • No routing file. New pools and model changes need no config change or restart.
  • Capacity and capabilities in MCS mode.
  • One PoolRelay contract for every --pool-discovery value.
  • Missed heartbeats drop a pool even when MCS still shows it Ready.
  • A warning when MCS and the registrations disagree for two lease durations, for example when cluster_id isn't the pool's MCS cluster name. Today such a pool is silently missing.
  • A warning when a live pool has no Ready frontend, for example when <dgd>-frontend isn't exported. Such a pool is listed with frontends: [], so routers have nowhere to send its requests.

Trade-offs

To deploy, each pool also exports <dgd>-frontend, as in #17276.

Validation

  • cargo test -p dynamo-global-discovery-service: 73 passed (61 library, 12 binary); the binary's flag tests also cover the frontend flags. They cover:
    • the join: only pools both sides list, which side each field comes from, watch events from both sides, a lapsed registration dropping a pool that MCS still lists, one-sided pools (one warning each, timed from when the pool became one-sided, and forgotten after a relist), a relist that drops a joined pool, watching both sides again when a watch cannot start or ends (watchers keep the last view, marked Estimated, until both sides list again, and the backoff then starts over), and its completeness, Exact only while both sides are;
    • frontends from MCS: frontend-only pools stay hidden, IPv6, a dual-stack pod listed once at its IPv4 address (also when its slices do not name it), only the named TCP port (and not port 0), a deleted frontend slice leaving a live pool without frontends, one warning per such pool, also across the reads before the first sync, and the check that the two Service suffixes differ;
  • Before the split, breaking each of 32 guarded behaviors on purpose made a test fail, for example one address family for frontends and the join's completeness. The peer ones (the self-skip, the readiness gate, the peer client's timeout, missing copies) moved to feat(global-router): share registrations between discovery instances #17637 with their tests. One bound has no test: the sweep runs at least once a second.
  • cargo clippy --all-targets -- -D warnings and cargo fmt on the crate: clean.
  • feat(global-router): add the Global Discovery Service library (contract and backends) #17331's and feat(global-router): add the Global Discovery HTTP service #17371's latest changes are merged in, and the frontend flags get the same checks as feat(global-router): add the Global Discovery HTTP service #17371's router flags: a suffix that starts with - goes after =, a suffix left without a value fails instead of taking the next flag, and no flag can be empty.
  • A second independent review found that the join always said Estimated, that a dual-stack frontend pod was listed twice, that a misnamed frontend port dropped a pool's frontends without a log line, and that the manifest's comments predated registrations. A third found that the join could then say Exact while another instance's registrations were missing, and that slices that do not name their pods still listed a dual-stack frontend twice. All are fixed.
  • A later review found four smaller issues, all fixed: overlapping frontend and PoolRelay suffixes were refused only after the Kubernetes client started, with exit code 1; a dual-stack pool's frontends followed whichever address family was Ready first; a frontend slice without the named port was warned about on every event; and the join's sweep test passed with any sweep period.
  • A review on the PR found that a source that could not be watched at startup would stop the join for good, and asked for a warning when a live pool has no frontend, for a warning text that gives the expected cause first, and for tests of a relist that drops a joined pool and of a deleted frontend slice. All are done: the join now watches both sides again after a backoff. An independent review of that fix found that the backoff never started over, that watchers kept a view marked Exact while the join watched the sources again, and that each read before the first sync repeated the frontend warning. Those are fixed too. Breaking each of the eight new guards on purpose made its test fail.
  • Not yet run on a cluster.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a Global Discovery Service that provides pool and router discovery information, health checks, and optional secure connections.
    • Pool discovery can combine active registrations with Kubernetes service endpoint data.
    • Added configurable HTTP server support for TLS, request limits, connection timeouts, and graceful shutdown, helping services handle traffic and deployments more reliably.
    • Added frontend load and readiness reporting, including request activity and model status.

…ts share

dynamo-global-common holds the types that more than one Global Router
component needs, so each is defined once:
- a pool's identity and location (PoolKey, PoolId and its deriver,
  PoolLocation), from the shared crate on the global-router-v2 test branch;
- the JSON of the Global Discovery Service's HTTP API: the POST /pools body
  and its lease reply, and the pool and router lists. Today the service and
  its client each define these;
- how a router names a PoolRelay on a trusted network (address_identity),
  which the service writes into each pool's relay_identity;
- Global Router replicas, and list-and-watch events and snapshots, which
  the service and the client in #17208 both define today.

The JSON does not change. PoolKey now reads and writes
{"site_id", "namespace", "dgd_name"} and checks each field while reading.
A lease reply parses only when the heartbeat interval is above zero and
below the lease, as in the client in #17208, and the lease is at most a
day.

Co-authored-by: Sachal Malick <s@chal.ai>
Co-authored-by: Jacob Ioffe <jioffe@nvidia.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@devin-ai-integration

devin-ai-integration Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

❌ Dynamo PR CI failed — run 41559139567 (attempt 1) on 451d353046

Gate checks: ❌ backend-status-check · ✅ deploy-status-check · ✅ dynamo-status-check

Framework 1-GPU arm64
vLLM ❌ 1

Failure details

1 job failed: both CPU pytest stages of the vLLM arm64 test job died right after pytest started, with the self-hosted runner's container hook error and no test results. This looks like infrastructure; rerunning the failed job is likely enough.

❌ vllm-runtime / Test cuda13.0, arm64: runner container hook failed ("Executing the custom container implementation failed")

Job: vllm-runtime / Test cuda13.0, arm64 · Failed step: Run CPU-only tests (parallelized) · Logs: gh run view --job 125680383412 -R ai-dynamo/dynamo --log-failed

▶️ Executing (cwd=/workspace): python -m pytest -n auto ... -m pre_merge and vllm and gpu_0 and not e2e
[Datadog Test Optimization] WARNING ... rerunfailures is installed alongside Datadog Test Optimization ...
##[error]Executing the custom container implementation failed. Please contact your self hosted runner administrator.
No files were found with the provided path: test-results/allure-results/. No artifacts will be uploaded.
▶️ Executing (cwd=/workspace): python -m pytest -n 0 ... -m pre_merge and vllm and gpu_0 and e2e
##[error]Executing the custom container implementation failed. Please contact your self hosted runner administrator.
...
❌ pre_merge_cpu: step outcome 'failure'
❌ pre_merge_cpu_e2e: step outcome 'failure'
##[error]A pytest stage did not record a passing result. See this step's log for the stage.

Neither pre_merge_cpu nor pre_merge_cpu_e2e printed a pytest result or test failure. Both ended with the runner's Kubernetes container hook error, and neither wrote allure results. That is an infrastructure signature (the runner or container exec failed), not a test failure, so a rerun of failed jobs is likely enough.

For agents
{"pr": 17332, "run_id": 41559139567, "run_attempt": 1, "head_sha": "451d3530462c824f8558866a69fcc7131f926524",
 "failures": [{"job": "vllm-runtime / Test cuda13.0, arm64", "job_id": 125680383412, "failed_step": "Run CPU-only tests (parallelized)", "signature": "Executing the custom container implementation failed (runner container hook, infra)", "tests": [], "log_cmd": "gh run view --job 125680383412 -R ai-dynamo/dynamo --log-failed"}]}

Posted automatically by Devin for run 41559139567. Updated on every full-CI run of this PR.

atchernych and others added 2 commits October 26, 2026 16:40
Review feedback: the comments on `round_trip`, `PoolLocation` and
`Snapshot` repeated the name or the fields right below them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…c comments

- A `POST /pools` body with an empty or blank `model` no longer parses, as
  with an invalid pool key, so the PoolRelay gets a 422 instead of
  registering a pool that no request can match.
- The crate docs said the service writes the API's JSON and its clients
  read it; the registration body goes the other way.
- `pool.rs` no longer refers to a source adapter that is not on main, or
  calls the V1 derivation the POC one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych marked this pull request as ready for review October 27, 2026 00:21
@atchernych
atchernych requested review from a team as code owners October 27, 2026 00:21
@atchernych
atchernych force-pushed the atchernych/discovery-pool-registrations branch from fa49608 to 8405ca5 Compare October 27, 2026 01:00
@atchernych
atchernych requested review from a team as code owners October 27, 2026 01:00
@atchernych
atchernych force-pushed the atchernych/global-discovery-service branch from 0ca36ea to 819368d Compare October 27, 2026 01:00
@atchernych
atchernych changed the base branch from atchernych/global-discovery-service to atchernych/global-discovery-http October 27, 2026 01:01
@atchernych
atchernych force-pushed the atchernych/global-discovery-http branch from 2487538 to f9f2f28 Compare October 27, 2026 01:53
@atchernych
atchernych force-pushed the atchernych/discovery-pool-registrations branch from 8405ca5 to 364b09e Compare October 27, 2026 01:53
@atchernych
atchernych marked this pull request as draft October 27, 2026 02:32
@atchernych
atchernych force-pushed the atchernych/discovery-pool-registrations branch from 364b09e to ee79333 Compare October 27, 2026 02:55
@atchernych
atchernych force-pushed the atchernych/global-discovery-http branch from f9f2f28 to b693840 Compare October 27, 2026 02:55
The one-day cap made the shared `Lease` stricter than the client in #17208:
once that client reads the shared type, a reply with a longer lease would
fail to parse. The cap only protects the service's own clock arithmetic, so
the service now bounds the lease it grants (#17371), and `Lease` keeps the
checks a client needs: the interval is above zero and below the lease.

Also drops `LeaseError`'s doc comment, which restated its use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych force-pushed the atchernych/discovery-pool-registrations branch from ee79333 to ba40a2f Compare October 27, 2026 03:38
@atchernych
atchernych force-pushed the atchernych/global-discovery-http branch from b693840 to 57067a7 Compare October 27, 2026 03:38
atchernych and others added 3 commits October 26, 2026 20:57
A pool's listing named its PoolRelay by one address. On a dual-stack
cluster the PoolRelay's pod has two, and it connects to a router from
whichever family the router's address uses, so a router matching one
address could turn the right PoolRelay away. `relay_identities` now lists
each of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
round_trip fails unless the parsed value serializes back to the same
JSON, so the field assertions after it could not catch anything more.
A lease of u64::MAX seconds already shows that a lease has no upper
bound, so the 48-hour case goes too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
dynamo-global-discovery-service starts as a library: which Global Router
pools exist, and which Global Router replicas a PoolRelay connects to.

Following the Global Router coding standards:
- The contract comes first: the store traits in pool_discovery
  (PoolDirectory, PoolRegistrar, ReplicaDirectory), on the
  dynamo-global-common types.
- The stores sit below it: in memory (registrations with leases), and MCS
  (pools and router replicas from the EndpointSlices that Karmada or a
  standard MCS implementation imports).

The HTTP service that serves them is added separately.

The code is the discovery work from #15933 and #15949, in the crate layout
of the global-router-v2 test branch. It leaves out the router-side pieces
those PRs had (relay admission, state connections, router discovery
modes): routers now read pools from the service, and relay connections
belong to the router.

Co-authored-by: Sachal Malick <s@chal.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
The join of registrations and MCS imports matches pools by key, so a
registration's cluster_id must be the name MCS gives the pool's cluster:
the About API cluster ID with standard MCS, or the member cluster name
with Karmada. Only the warning for an unmatched registration said so;
the module docs now do too.

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

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

lgtm

couple of questions:

  • frontend_url is always http:// (hub.rs:200). is tls on the frontend out of scope for v1, or should the scheme come from the port's appProtocol?
  • where do poolrelays get the mcs cluster name that cluster_id has to match?

atchernych and others added 5 commits October 30, 2026 13:01
A pool runs active-active PoolRelay replicas: each reports to every Global
Router, and routers dedupe the overlapping reports, as the operator's
DynamoPoolRelay runs two pods and #17686 keeps their reports apart. The
MCS backend bound only the Ready pod with the lowest address, so routers
matching relay_identities would admit one replica and refuse the other.

It now lists every Ready PoolRelay pod by each of its addresses, sorted,
so every service instance lists the same identities. A pool stays live
while any PoolRelay endpoint is Ready, and a pod that stops being Ready
leaves the listing. An IPv4-mapped IPv6 address counts as its IPv4
address, so a pod is never listed twice under one identity. The pods were
grouped by targetRef only to choose one, so the grouping is gone.
PoolListing::relay_identities now says it names every replica, and the
memory store says that replicas registering one pool should send the same
registration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…obal-discovery-http

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…very-pool-registrations

Brings in the listing of every Ready PoolRelay pod. In this branch's MCS
backend, which also reads frontend slices, ready_addresses returns plain
addresses for both kinds of slice, and rebuild lists every Ready PoolRelay
address, canonicalized, next to the frontends. A test now checks that
frontend addresses are not PoolRelay identities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
A pool runs active-active PoolRelay replicas, so the join lists a pool
while MCS imports a Ready PoolRelay endpoint and one of its PoolRelays has
registered, and MCS gives every Ready PoolRelay's addresses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Fixes from review of the MCS backend with registrations:

- The join gave up for good when it could not watch a source: every
  watch stream ended, and /healthz stayed OK. It now watches both
  sources again with kube's default backoff (0.8 s doubling to 30 s),
  also when a source's watch ends, and joins on each read until both
  have listed again. A rebuild after both sources listed again now also
  drops a pool that neither lists any more.
- The MCS backend warns when a pool goes live without a Ready frontend,
  or loses its last one. The warning names the frontend Service and says
  whether any of its slices are imported, so a frontend Service that is
  not exported shows up. It is logged once per change, not on every
  slice event.
- The warning for a pool that MCS lists but that has no live
  registration gives the cause the module describes first: a lease that
  lapsed while MCS still shows a stale Ready endpoint.
- Tests: a source relist that drops a joined pool removes it from the
  join and from its Resync; a failed subscription and an ended watch are
  watched again; deleting a frontend slice lists the pool without
  frontends.

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

atchernych commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review, Eric. The five inline comments are addressed in 2ba2b8b, with a reply in each thread. On your two questions:

TLS to the frontends. Out of scope for V1, which assumes a trusted network, as the ip: relay identities do. When we add it, the scheme should come from appProtocol, as you suggest: the operator would set appProtocol: https on the frontend Service's port when the frontend serves TLS (it sets none today), Karmada copies ports into the slices it imports, and this service would build https:// URLs from it. Routers would also need to trust the frontends' certificates for the pod addresses they connect to, which is the larger part. I can open an issue for it.

Where PoolRelays get the cluster name. From DynamoPoolRelay.spec.clusterId, which the operator passes to the Relay container as --cluster-id. A person is expected to set this manually
With standard MCS, #17412 compares it with the About API's ClusterProperty cluster.clusterset.k8s.io and sets the DGD's GlobalRouterPoolExported condition to False, with reason ClusterIDMismatch, when they differ.

With Karmada the operator doesn't check it, since Karmada names clusters by their member names, so clusterId has to be set to that name by hand, and this service's one-sided warning ("check that cluster_id is the pool's MCS cluster name") is what shows a mismatch. Whether the operator should fill it in is open question 4 in #17600.

atchernych and others added 6 commits October 30, 2026 14:55
…arning once

Follow-ups from an independent review of the previous commit:

- The join's backoff starts over once the join follows both sources
  again, as kube's StreamBackoff does once its watch delivers. Before, a
  watch that kept ending every minute would have waited 30 s each time.
- While the join watches the sources again, its watchers get the last
  view marked Estimated, as the MCS backend's watchers do while its
  watch fails, instead of keeping a view marked Exact.
- The warning for a live pool without a frontend is recorded in the
  naming rules rather than the index, so the new index that each read
  builds before the first sync does not log it again. The log field is
  now frontend_slices_with_port, since a frontend slice without the
  named port does not count.
- The retry test checks that the first attempt failed before it checks
  that each read joins the sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
POST /pools took bodies of up to axum's default of 2 MiB, and the
backend keeps each registration for its lease. A registration is a few
hundred bytes, so the routes now refuse a body over 64 KiB with 413
before it reaches the backend. A test sends one body at the limit and
one a byte over it.

The module doc now says that ready does not mean the pool list is
complete: GET /pools says that in `complete`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…very-pool-registrations

Brings in the 64 KiB limit on request bodies and the readiness note in
the module doc. The note sits after this branch's text on partial lists
under GET /pools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
#17331 is in main as one squashed commit, so its files came from both
sides. Main has not changed them since, so this branch's versions, which
add the HTTP service to them, stand. Cargo.lock is main's, plus
dynamo-http-server and the service's new dependencies. CODEOWNERS
regenerates unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
The example apply command named global-discovery-service.yaml, which
works only from container/. It now names
container/global-discovery-service.yaml, so it runs from the repository
root, like the build command in the Dockerfile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…very-pool-registrations

Brings in main, which the base branch merged after #17331 landed, and
the manifest's apply command from the repository root. In the manifest's
header, this branch's text on what the service lists stays, after the
new command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Base automatically changed from atchernych/global-discovery-http to main November 2, 2026 21:59
@atchernych
atchernych requested review from a team as code owners November 2, 2026 21:59
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between b751c95 and ea29da0.

Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 26
  • .github/codeowners/areas.yaml
  • .github/filters.yaml
  • CODEOWNERS
  • Cargo.toml
  • container/Dockerfile.global-discovery-service
  • container/global-discovery-service.yaml
  • lib/global-discovery-service/Cargo.toml
  • lib/global-discovery-service/src/backends.rs
  • lib/global-discovery-service/src/backends/kubernetes.rs
  • lib/global-discovery-service/src/backends/kubernetes/endpoints.rs
  • lib/global-discovery-service/src/backends/kubernetes/hub.rs
  • lib/global-discovery-service/src/backends/kubernetes/mcs.rs
  • lib/global-discovery-service/src/backends/memory.rs
  • lib/global-discovery-service/src/backends/registered.rs
  • lib/global-discovery-service/src/lib.rs
  • lib/global-discovery-service/src/main.rs
  • lib/global-discovery-service/src/service.rs
  • lib/http-server/Cargo.toml
  • lib/http-server/src/disconnect.rs
  • lib/http-server/src/lib.rs
  • lib/http-server/src/lifecycle.rs
  • lib/http-server/src/load.rs
  • lib/http-server/src/metrics.rs
  • lib/http-server/src/request.rs
  • lib/http-server/src/server.rs
  • lib/http-server/src/write_timeout.rs
Files not fully reviewed because their changes were unavailable: 1
  • lib/http-server/src/metrics.rs

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Walkthrough

The change adds a reusable HTTP server crate and a Global Discovery Service. The service combines registration and Kubernetes discovery data, exposes pool and router HTTP endpoints, and adds container and Kubernetes deployment configuration.

Changes

HTTP server and Global Discovery Service

Layer / File(s) Summary
HTTP server crate and shared data types
Cargo.toml, lib/http-server/*
The workspace adds the HTTP server crate. The crate exposes load and request utilities and declares its public modules.
Connection and request lifecycle
lib/http-server/src/disconnect.rs, lib/http-server/src/lifecycle.rs
Connection handles report closure status to a monitor. The service observer tracks readiness stages and inflight responses.
HTTP server, shutdown, and write deadlines
lib/http-server/src/server.rs, lib/http-server/src/write_timeout.rs
The server handles TCP and TLS connections, transfer and write-stall deadlines, and graceful shutdown. Tests cover listener configuration and timeout behavior.
Kubernetes pool and frontend discovery
lib/global-discovery-service/src/backends/kubernetes/*, lib/global-discovery-service/src/backends/memory.rs
The Kubernetes backend builds pool listings from Ready relay endpoints and frontend addresses in EndpointSlices. It validates service naming and tracks frontend availability warnings. The memory backend caps its sweep interval at one second or the heartbeat interval, whichever is shorter.
Joined registration and MCS listings
lib/global-discovery-service/src/backends/registered.rs, lib/global-discovery-service/src/backends.rs
The registered pool directory includes only pools present in both source directories. It combines their listing data, tracks completeness, and retries failed or ended watches.
Discovery HTTP API
lib/global-discovery-service/src/service.rs, lib/global-discovery-service/src/lib.rs
The service adds pool and router routes, optional pool registration, health checks, and error responses. Tests cover route behavior, request limits, unavailable backends, and shutdown.
Discovery service configuration and startup
lib/global-discovery-service/Cargo.toml, lib/global-discovery-service/src/main.rs
The binary adds discovery modes, CLI validation, backend construction, HTTP startup, and signal-based shutdown.
Container and deployment configuration
container/Dockerfile.global-discovery-service, container/global-discovery-service.yaml, .github/codeowners/areas.yaml, .github/filters.yaml, CODEOWNERS
The change adds a container image and Kubernetes resources for the service. Ownership rules cover the new paths, and the CI ignore filter includes the Dockerfile and manifest.

Estimated code review effort: 4 (Complex) | ~60 minutes

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 46.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly describes the primary change: Global Discovery Service pool models now come from registrations instead of a routing file.
Description check Passed The description is detailed and covers the change overview, key behavior, validation results, trade-offs, ongoing limitations, and related issues. It does not use the exact template headings or includ…
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.
Full details: Description check

Explanation

The description is detailed and covers the change overview, key behavior, validation results, trade-offs, ongoing limitations, and related issues. It does not use the exact template headings or include the checklist, but the required information is mostly present.

Full details: Docstring Coverage

Explanation

Docstring coverage is 46.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 80 functions across 17 files. (8 skipped: 8 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Review coverage is incomplete: 1 file could not be fully reviewed. Findings from completed review steps are included; see review info for details.

Brings in main, which now has #17371 (squash-merged as b751c95).
This branch already had all of #17371 through 7f61b37, so the four
files that conflicted keep this branch's version: the manifest,
backends/kubernetes.rs, main.rs and service.rs. Main has not changed
them since #17371, so the merged tree is main plus this branch's own
changes, and nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych removed request for a team November 3, 2026 01:24
@atchernych
atchernych enabled auto-merge (squash) November 3, 2026 16:30
Brings in main up to c80a17b. None of main's eleven new commits
touch this branch's files or the crates it depends on, so the merge is
clean, and the merged tree is main plus this branch's own changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@atchernych
atchernych merged commit d1cf542 into main Nov 3, 2026
124 checks passed
@atchernych
atchernych deleted the atchernych/discovery-pool-registrations branch November 3, 2026 17:54
atchernych added a commit that referenced this pull request Nov 3, 2026
Brings in main, which now has #17332 (squash-merged as d1cf542). This
branch already had #17332 through d8f5672, so the merge takes that
commit as its base: the merged tree is main plus this branch's own
changes, and nothing else. The files that a plain merge reports as
add/add conflicts take that tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants