Repository navigation
feat(global-router): add the Global Discovery Service library (contract and backends) - #17331
Conversation
…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>
✅ Dynamo PR CI passed — run 41120860737 (attempt 2) on
|
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>
0ca36ea to
819368d
Compare
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>
388d5bb to
444711b
Compare
There was a problem hiding this comment.
Previously reported defects still present:
- Original discussion: Verified still present:
SliceCache::applyclearsfailing_sincebefore matching the event, so repeated relists that emitInitorInitApplybut fail beforeInitDonecontinually reset the outage timer and can keep a stale snapshot classified asExactindefinitely. - Original discussion: Verified still present: watch failures only update
failing_since; crossingSTALE_AFTERdoes not broadcast anEstimatedresync, and recovery does not necessarily broadcast anExactresync when membership is unchanged. Existing subscribers can therefore retain obsolete completeness indefinitely.
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>
- When the EndpointSlice watch keeps failing, for example after its RBAC is removed, the cache served its last view as complete indefinitely. It still serves that view, and the service stays ready, but after a minute of failures it reports the view as Estimated, so GET /pools says "complete": false, and it logs the failures as errors. - A misnamed router port, Service or namespace left GET /routers empty with nothing in the log. A router EndpointSlice without the named port is now logged, and so is a relist that lists nothing, which also catches a cluster without the expected ServiceImports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
… list Review feedback: - list_and_watch returns a stream that yields nothing until the first complete list. That is deliberate, as dynamo_runtime's Kubernetes discovery also waits to be ready, but it was undocumented. The cancel token ends the wait, and the watch's failures are logged. - The error logged after a minute of watch failures claimed to list the last view as incomplete even before there was a view. - A test comment only restated the next line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…ache - The watcher's lists, including its relists after a watch error, were quorum reads from etcd (kube's default, ListSemantic::MostRecent), paged 500 at a time. They now use any_semantic(): the API server answers from its watch cache, and the watch catches up from there. Discovery needs nothing stronger, and the pool watch covers every imported EndpointSlice in the cluster. - The one list made before the cache syncs uses match_any() for the same reason. - The pool and router indexes each had a copy of the Ready filter; both now use one ready_endpoints helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
… them The previous commit asked the watcher for lists from the API server's cache with any_semantic(), but kube sends resourceVersion=0 only when no page size is set, and the watcher pages 500 at a time by default. Its lists therefore stayed paged quorum reads from etcd. With paging off they are served from the watch cache in one response, as client-go's reflector lists. The cache keeps only what it derives from each slice, so the larger response costs little. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Main now has the Global Discovery Service client from #17208 in lib/global-discovery-client, which the next commits move onto this crate's types, and the PoolRelay rename from #17215. No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
From Neelay Shah's review: - PoolKey's first field is cluster_id, not site_id, in Rust and in JSON. It is the cluster ID the pool's PoolRelay runs with (--cluster-id, DYN_CLUSTER_ID), so a pool key and its relay share one name. Where MCS also reports the pool, that ID must be the cluster's name in the ClusterSet, and the doc says so. Pool IDs do not change: the deriver hashes the values, not the field names. - PoolLocation drops cluster, which the key carries. - ReplicaEndpoint, ReplicaId and ReplicaEvent become RouterReplica, RouterReplicaId and RouterReplicaEvent, since relays and workers have replicas too. Their JSON does not change. - WatchEvent::Added becomes Upserted, Lease::lease_duration() becomes duration(), and Capacity.count becomes workers, in JSON too. Lease is now #[must_use], as the client's copy was, so the client keeps that check when it moves onto this type. From the review threads: a test, from Dmitry Tokarev's review, that a reader skips unknown fields and fills missing lists, and two duplicate assertions removed. A pre-push review added a test that a reply without its list does not parse, so an empty body never reads as "no routers". Co-authored-by: Dmitry Tokarev <191198572+dmitry-tokarev-nv@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Neelay Shah and Jacob Ioffe asked on #17208 that its client move onto dynamo-global-common in this PR, so two PoolKey definitions never sit on main together. lib/global-discovery-client drops its own copies of the JSON types, router replicas and watch events. It re-exports the shared types its calls send and return, with their error types; the watch types come from dynamo-global-common. Its calls and errors do not change. What it sends follows the shared types: the key's cluster_id, a location without cluster, and capacity in workers. A PoolKey is built with PoolKey::new, which checks its fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…ery-service The base renamed the shared types after review (PoolKey's cluster_id, RouterReplica, WatchEvent::Upserted, Lease::duration, Capacity.workers, no PoolLocation.cluster), moved the discovery client onto them, and merged main. No conflicts; the next commit follows the renames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
The base renamed them after review: PoolKey::cluster_id (was site_id), RouterReplica, RouterReplicaId and RouterReplicaEvent (were ReplicaEndpoint, ReplicaId and ReplicaEvent), WatchEvent::Upserted (was Added), Lease::duration (was lease_duration) and Capacity.workers (was count), in JSON too. PoolLocation has no cluster field, so nothing sets it. Lease is #[must_use], so tests that only register drop the lease explicitly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Shared identity and discovery types Cargo.toml, lib/global-common/* |
Adds shared pool identity and location types, discovery request and response types, replica types, and watch events. Adds validation, serialization, and derivation tests. |
Discovery service contracts lib/global-discovery-service/src/pool_discovery*, lib/global-discovery-service/src/lib.rs, lib/global-discovery-service/src/backends.rs, lib/global-discovery-service/Cargo.toml |
Adds pool registration and directory contracts, watch-stream types, a discovery error type, and public service modules. |
Client migration to shared types lib/global-discovery-client/* |
Moves client-facing wire and replica types to dynamo-global-common. Updates the router response type and relay-call test fixtures. |
In-memory stores and watch streams lib/global-discovery-service/src/backends/memory.rs, lib/global-discovery-service/src/backends/stream.rs |
Adds lease-based pool registration and expiration, a static replica directory, and watch-stream helpers for initial snapshots and lag recovery. |
Shared Kubernetes slice cache lib/global-discovery-service/src/backends/kubernetes/cache.rs |
Adds a shared EndpointSlice cache with relisting, watch-error completeness updates, retry handling, and directory listing and watch operations. |
MCS replica discovery lib/global-discovery-service/src/backends/kubernetes.rs, lib/global-discovery-service/src/backends/kubernetes/mcs.rs, lib/global-discovery-service/src/backends/kubernetes/endpoints.rs |
Adds Standard and Karmada slice naming support and replica discovery from imported service endpoints. |
MCS pool discovery lib/global-discovery-service/src/backends/kubernetes/hub.rs |
Adds pool listing and watch behavior derived from imported slices, including relay selection, liveness, and completeness updates. |
Estimated code review effort: 4 (Complex) | ~60 minutes
Merge Risk: 
Minimal · up to 69d70
-
This change adds the pool discovery contracts, shared types, and in-memory and MCS-backed stores, and migrates the client to the shared types. No concrete merge-blocking defect was identified. Cluster-level validation of the Kubernetes backends is still pending and should be done in normal rollout.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Description check
-
Explanation
The description is detailed and covers the change overview, key components, implementation modes, validation results, known limitations, and related issues. It does not use every template heading exactly and omits the checklist and explicit Issues and Ongoing Work section, but the required technical information is mostly complete.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 52.27% 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 44 functions across 22 files. (4 skipped: 4 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
Comment @coderabbitai help to get the list of available commands.
…asked
The review bot found two test comments that only repeat the code after
them ("The same slice again changes nothing."), so they are removed.
CodeRabbit's docstring check counts functions that branch. The four
outside tests that had no doc comment now have one: SliceCache::subscribe,
the replica index's replace, and both list_and_watch in the memory
backend.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Karmada v1.19 imports a Service as derived-<name>, and puts that name in each imported slice's kubernetes.io/service-name label. A Service name and a label value may have at most 63 characters, so Karmada imports nothing for a name over 55: neither the derived Service nor its slices appear. The store used 57, the length at which Kubernetes starts to shorten slice names, but Karmada already fails at 56. KARMADA_MAX_SERVICE_NAME is now 63 minus the "derived-" prefix, and a test pins it at 55. McsReplicaDirectory logs an error when Karmada cannot import the router Service, since none of its replicas would be listed. The warning for a slice that does not say its cluster no longer blames the Service name's length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…vice McsReplicaDirectory reads any exported Service, not only the router's; the PR stacked on this one also reads the service's peers through it. Its error for a name Karmada cannot import, and the module doc that points to it, no longer say "router". The doc also names the default suffix behind the 44-character DGD limit, -pool-relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…very-pool-registrations The base took #17331's changes: main (with #17330's shared types), doc comments on the stores' watch streams, and Karmada's Service name limit, 55, not 57. McsReplicaDirectory, which also reads the peer Service here, now logs an error for a Service name Karmada cannot import. No conflicts. CODEOWNERS still matches areas.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…very-register-router The base took #17331's changes: main (with #17330's shared types), doc comments on the stores' watch streams, and Karmada's Service name limit, 55, not 57. No conflicts. CODEOWNERS still matches areas.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
… stores What implements the pool discovery contract is a backend: MCS is read, not stored into. The crate, contract and MCS docs now say "backend". The memory backend's docs still call MemoryPoolStore a store, since it does keep registrations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
estroz
left a comment
There was a problem hiding this comment.
LGTM on initial pass, may have some follow-ups
The stream's docs said it waits until the source has "a complete view", which reads like `Completeness::Exact`. It means the source has listed everything once: for MCS, kube's `InitDone`. Whether a list holds every item is its `completeness`, which each `Resync` carries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
A list's completeness says whether it may be missing items, and `GET /pools` passes it on as `complete`. The contract now says what a caller that keeps its own pool list does with each: replace on a complete list, merge a partial one and keep missing pools, and still use a partial list when it knows no pools yet. The design link points at the DEP issue, #17600, since enhancements #103 is closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
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: 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>
dmitry-tokarev-nv
left a comment
There was a problem hiding this comment.
No P0 or P1 found, and this review posts no findings. It covers head 73326260bcecf6edf2b4502039bd564304a1a8df, and a local merge of that head into main at ba9caeac492cdeef00fbb6096ae6efda40f81064, which merges without conflicts.
Open items: none. All earlier threads are resolved.
Tests, lock file and deliberate breaks, on the head and on merges with main.
cargo test --locked -p dynamo-global-discovery-service: 39 passed on the head, and on merges withmainat3057891245and atba9caeac49.cargo tree --lockedpasses on the head and on both merges. The lock adds no package. Only the dependency list ofdynamo-global-discovery-servicechanges, and every crate in it is already locked onmainat the same version.- Removing
registered.expires_at = expires_at;atmemory.rs:209failsregistering_again_only_renews. - Changing
>=to>atmemory.rs:122failslists_are_exact_once_a_lease_has_passed. - Changing
>=to>atcache.rs:167fails 4 tests inhub.rs. - CI at this head, at the time of this review: the
PRrun onpull-request/17331passed. In Pre Merge,rust-tests (.)was still running, and the other jobs passed.
Memory backend, measured with paused tokio time and with 8 worker threads.
- The lease uses
tokio::time::Instant, a monotonic clock. - With a 5 s / 15 s lease, a pool is listed at 14.999 s and gone at 15.000 s. A second registration at 10 s moves that to 24.999 s and 25.000 s. The sweep sends
Removedat 15 s, with theResyncthat marks the listExact. - 32 tasks sent 16,000 registrations of one pool. With one payload, the store keeps one listing and sends no event after the first. With two alternating payloads, it sends more events than the 1,024-event buffer holds, and the watcher gets a fresh
Resync, as the docs say. - The store has no bound on the number of pools: 100,000 distinct pools registered in 638 ms, and
list()returned them in 61 ms. - Memory: 1,224 heap bytes per pool for a 291-byte registration, plus 661 bytes per pool for each
list()copy. For a registration with 1,000 capabilities of 100 characters, eachlist()copy takes about 125 KB per pool, and the store keeps about the same.
MCS backend, against an in-process fake of the EndpointSlice API on 127.0.0.1, with the 10 s / 75 s client timeouts from #17371.
- Pools, Karmada names: Ready endpoints and endpoints without
readyare listed. NotReady endpoints are not, and a pool with only NotReady relays is not listed. A pool appears, grows and leaves throughUpsertedandRemovedas its endpoints change. - Pools, standard
multicluster.kubernetes.iolabels: listed from the same fake, and slices named the other way are skipped. - Router replicas: both naming styles list Ready endpoints only. A 55-character Karmada Service is listed. A 56-character one logs the error from
McsReplicaDirectory::new. Its list then fails, because the fake refuses a label value over 63 characters, as the API server does. - An empty first list, and an empty relist after a 410, both send
Resyncwith no items and log the warning. - With the API server failing every request, the
ResyncmarkedEstimatedcame 80.4 s after the first error. Requests came at 0.8, 1.6, 3.2, 6.4, 12.8 and 25.6 s intervals, then every 30 s. The check runs only at an error. The first error past one minute came at 80.4 s, and until thenlist()stayedExact. - Recovery: a changed slice brought
Upsertedand anExactResync30.0 s after the API server recovered. With no change, theExactResynccame 120.0 s after the last error. - Every request went to the fake. The client had no other API server address.
dmitry-tokarev-nv
left a comment
There was a problem hiding this comment.
Reviewed at 73326260bcecf6edf2b4502039bd564304a1a8df, and on a local merge with main at ba9caeac492cdeef00fbb6096ae6efda40f81064, which has no conflicts.
- Tests: 39 of 39 pass on both trees. Three deliberate one-line breaks each fail a test: the lease renewal, the completeness boundary of the memory backend, and the staleness boundary of the MCS cache.
- Lock file: no new crate, and
cargo tree --lockedpasses on both trees. - Memory backend: lease expiry and renewal are exact on the monotonic tokio clock. 16,000 concurrent registrations of one pool keep one listing. The number of pools has no bound (100,000 registered in 638 ms), at about 1.2 KB per pool.
- MCS backend, against an in-process fake of the EndpointSlice API: Ready and NotReady endpoints, both naming styles, the 55-character limit, empty relists, and the
EstimatedandExactresyncs all match the description.
Open items: none. #17371 stacks on this PR.
Summary
Adds
lib/global-discovery-serviceas a library: the contract for which Global Router pools exist and which router replicas a PoolRelay connects to, and the backends behind it. The HTTP service and its binary are in #17371. The crate's name and place follow #17354, and its shared types come fromlib/global-common(#17330, merged).Part of #17600, the pool discovery DEP.
We have 2 knobs in discovery:
--pool-discovery = registrations or karmada or mcs: where the pool and router lists come from. The registrations means the data comes from the API calls i.e. POST /pools
--registration-storage = memory or DynamoDB or Kubernetes : where registrations are kept.
Layout (Global Router coding standards)
pool_discovery:PoolDirectorylists and watches pools,ReplicaDirectorylists and watches router replicas, andPoolRegistrarregisters a pool. Each registration is also the pool's heartbeat.backends:relay_identitiesname every Ready PoolRelay pod by each of its addresses, sorted, so every service instance lists the same pods: a pool runs active-active PoolRelay replicas, each reports to every router, and routers dedupe the overlapping reports.Resyncat both changes, and a relist counts only once it completes.derived-<name>, which must fit in 63. The router directory logs an error for a longer name, and the operator in feat(operator): export Global Router pools through the Multi-Cluster Services API #17412 does not export one.Where the code comes from: #15933 and #15949, in the crate layout of the
global-router-v2test branch; this PR replaces both. Their router-side pieces (relay admission, state connections, router discovery modes) are left out, since relay connections belong with the router. #17355 already added the empty crate and its code-owner rule, so this PR adds no workspace member or code-owner line.Main components
pool_discovery)backends)PoolDirectory(list,list_and_watch)MemoryPoolStore); MCS (McsPoolDirectory, Karmada or standard)PoolRegistrar::registertakes aPoolRegistrationand returns itsLeaseReplicaDirectoryStaticReplicaDirectory, set by--routerin #17371); MCS (McsReplicaDirectory)PoolKey,PoolLocation,PoolRegistration,PoolListing,Lease,WatchEvent,Snapshot,Completeness,RouterReplicalib/global-common(#17330)Per mode
The HTTP service in #17371 picks the backend with
--pool-discovery.--pool-discovery registrations)--pool-discovery karmadaormcs)PoolDirectory(discovery service)<DGD name>-pool-relayby default), on the Karmada control plane or in a cluster that imports them. A pool is live while one of its PoolRelay endpoints is ReadyPoolRegistrar(discovery service)POST /poolsreturns 501 (#17371), so records have no model, frontends, or capacity. #17332 takes the model and capacity from registrations, and the frontends from MCSrelay_identitiesare the addresses of every Ready PoolRelay pod, for the router to match against the caller's source IPReplicaDirectory(discovery service)GET /routerslists the replicas set with--router(#17371). Routers cannot register yet (#17333)GET /routerslists the Ready replicas in the router Service's imported EndpointSlices. The router's Service is exported once (admin setup)POST /poolson a timer, throughlib/global-discovery-client(#17208). No PoolRelay calls it yetServiceExportin draft #17412. With #17332 the PoolRelay also callsPOST /poolsGET /routers, through the same clientGET /routers. The PoolRelay reads no EndpointSlicesValidation
cargo test -p dynamo-global-discovery-service: 39 passed. They cover the memory backend's leases and completeness, the MCS pool and replica indexes (dual-stack PoolRelays and replicas, every Ready PoolRelay pod listed alike by every instance, a pod that stops being Ready leaving the listing, an IPv4-mapped address listed once), Karmada's name limit, a watcher that falls behind, and failing watches: going stale, recovering by silence, by an event or by a relist, a relist that fails partway, and backoff. Breaking each behavior the reviews fixed makes a test fail.cargo clippy --all-targets -- -D warnings,cargo fmt, andcargo docwith-D warnings: clean.dynamo_runtime's Kubernetes watch code would need a refactor ofdynamo-runtime, so it is not done here.resourceVersion=0, unpaged), watches resume with bookmarks, and after the first syncGET /poolsandGET /routersmake no API calls.🤖 Generated with Claude Code
Summary by CodeRabbit