feat(llm): add pool-scoped KV DC Relay runtime - #12199
Conversation
81f9a6c to
f108671
Compare
WalkthroughThe relay runtime now models CKF ownership by contributor and physical pool, discovers domain-slot memberships, manages shared pool actors through a registry, distributes recoverable publication updates, and updates host lifecycle, diagnostics, and configuration handling. ChangesKV DC Relay runtime
Estimated code review effort: 5 (Critical) | ~120 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (8)
lib/llm/src/kv_dc_relay/host.rs (1)
745-759: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFailed registration refresh warns on every loop iteration.
On error
active.registrationsstays stale, so the mismatch persists and this branch re-runs on eachSlotInput— including the periodicHealthtick at Line 911 — emitting an identical warning indefinitely for a durable conflict (e.g. an alias claimed by another pool). Consider rate-limiting (first occurrence atwarn, subsequent atdebug, like thestart_failurespattern at Line 856).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/host.rs` around lines 745 - 759, The registration refresh error path in the loop around pools.replace_registrations must avoid warning on every SlotInput/Health iteration. Add persistent failure tracking for this refresh, following the existing start_failures pattern near the relevant host logic: log the first failure at warn, subsequent identical failures at debug, and reset the tracking after a successful replacement.lib/llm/src/kv_dc_relay/pool_registry.rs (2)
592-601: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
publication_hubsis written but never read.
refresh_publication_hubsis called fromattach,detach,replace_registrations, andfence, and the vector is cleared inshutdown, but nothing ever readsself.publication_hubs. Every mutation also sorts and clones hubs while holding the registry state lock. If this is staged for the next layer, mark it explicitly (likePoolCatalogat Line 101) or drop it until a consumer exists.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/pool_registry.rs` around lines 592 - 601, The publication_hubs field updated by refresh_publication_hubs is currently unused; either mark it explicitly as intentionally staged, matching the existing PoolCatalog convention, or remove publication_hubs, refresh_publication_hubs, and their mutation/cleanup calls until a consumer is implemented. Prefer removing the unused sorting and hub cloning if no near-term consumer is required.
808-840: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueCross-pool validation is O(pools × contributors × registrations) per registration.
For each incoming registration the loop rescans every contributor's registrations in every pool. With a large fleet this makes
attach/replace_registrationsquadratic while holding the globalstatemutex. AHashMap<CanonicalModelId, ModelTarget>maintained alongsidecanonical_refcountswould make the target-conflict check O(1).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/pool_registry.rs` around lines 808 - 840, Update validate_registrations to avoid traversing every pool, contributor, and existing registration for each incoming registration. Maintain and reuse a HashMap<CanonicalModelId, ModelTarget> alongside canonical_refcounts in PoolRegistryState, and perform existing-registration target conflict checks through that index in O(1) per registration while preserving duplicate and conflict errors for both the current attachment and prior registrations.lib/llm/src/kv_dc_relay/publication_hub.rs (1)
163-196: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
subscribe/snapshotareasyncwithout awaiting.Both take a synchronous
parking_lotlock and never await. Making them sync keeps the API honest and avoids callers assuming a yield point.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/publication_hub.rs` around lines 163 - 196, Make PublicationHub::subscribe and the test-only snapshot method synchronous by removing async from their signatures. Update all call sites and tests to stop awaiting these methods while preserving their existing locking, readiness checks, and return values.lib/llm/src/kv_dc_relay/discovery.rs (3)
841-866: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
list_queriesandopen_query_streamsserialize one round trip per namespace.Both run sequentially, so startup and every 30s reconcile cost N sequential discovery calls.
futures::future::try_join_allwould bound this to a single round trip; worth doing if the namespace list is expected to be more than a handful.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/discovery.rs` around lines 841 - 866, Update list_queries and open_query_streams to initiate discovery operations for all queries concurrently using futures::future::try_join_all rather than awaiting each request inside the loop. Preserve error propagation and aggregate the returned instances or DiscoveryStream values into the existing result types.
1181-1208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNo test covers the ambiguous-name
Bindingconflict path.Lines 618-657 are the most intricate new logic — one request-facing name resolving to two different
BindingIdentitytargets must produce aMembershipConflict::Bindingand drop the claim — and it also has to stay in step withvalidate_registrationsinpool_registry.rs. A case where an alias on one card collides with a canonical model name on another would lock that contract in.Want me to draft that test?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/discovery.rs` around lines 1181 - 1208, Add a test covering the ambiguous-name Binding conflict path in the discovery logic around the existing alias tests: create two cards where an alias on one card matches the canonical model name on the other, apply both registrations, and assert the shared request-facing name produces MembershipConflict::Binding with the claim dropped. Keep the expectations aligned with validate_registrations in pool_registry.rs.
483-503: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdapter cards with no backing base card disappear without any diagnostic.
When
worker_basesis empty,worker_domain_idsis empty too, so the conflict loop at Line 491 iterates zero times and the adapter is dropped silently — no conflict entry, no log. That is the likely steady state while a base card is still propagating or has just been removed, so a debug log (or a conflict on every domain of the endpoint) would make the transient visible inmembership_conflicts.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/kv_dc_relay/discovery.rs` around lines 483 - 503, The conflict handling around worker_domain_ids must preserve diagnostics when worker_bases is empty instead of silently dropping the adapter. Update the branch using worker_domain_ids, ambiguous_workers, and conflicting_worker_facts so the no-backing-base case records a MembershipConflict::Card for an appropriate endpoint domain (or otherwise emits the requested debug diagnostic), while retaining existing conflict behavior for populated domain IDs.lib/kv-router/src/indexer/cuckoo/dc.rs (1)
828-836: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueLeftover "worker" vocabulary after the source-keyed rewrite. The membership key changed from
WorkerWithDpRanktoDcCkfSourceId, but two surfaces still speak in worker terms, which is exactly the aliasing this PR eliminates.
lib/kv-router/src/indexer/cuckoo/dc.rs#L828-L836: rename theworker: DcCkfSourceIdparameter onstore(and the matching one onremove/remove_member) tosource, matchingremove_sourceandsource_counts.lib/kv-router/src/indexer/cuckoo/dc.rs#L793-L801: documentmember_countsas the legacy unscoped view, or fold duplicates, since two contributors reusing a worker/rank now collapse to identicalWorkerWithDpRankentries.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/kv-router/src/indexer/cuckoo/dc.rs` around lines 828 - 836, The source-keyed rewrite still exposes worker terminology and an ambiguous member_counts view. In lib/kv-router/src/indexer/cuckoo/dc.rs lines 828-836, rename the store parameter and corresponding remove/remove_member parameters from worker to source, updating their uses to match remove_source and source_counts; in lines 793-801, document member_counts as the legacy unscoped view or fold duplicate entries caused by contributors sharing a worker/rank.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/llm/src/kv_dc_relay/discovery.rs`:
- Around line 702-720: Remove the per-view pruning of self.domain_generations
after rebuilding domains so counters remain monotonic for slots that temporarily
disappear and later reappear. Keep generation updates in the existing changed
branch and preserve the current cleanup of warned_invalid_models and
warned_invalid_aliases.
- Around line 49-81: Normalize each namespace and endpoint prefix by trimming
surrounding whitespace before validation, deduplication, containment checks, and
any downstream query construction. Update the validation logic in the relevant
discovery configuration method so `" prod"` and `"prod"` are treated
identically, while preserving the existing empty, duplicate, and
namespace-boundary errors.
- Around line 736-737: Update the stream handling around open_query_streams so
termination of any individual query stream causes the outer discovery/watch loop
to break and rebind all streams. Wrap or otherwise monitor each substream before
it is consumed by SelectAll, while preserving normal message handling and
cancellation behavior.
In `@lib/llm/src/kv_dc_relay/identity.rs`:
- Around line 277-321: Update the pool helper and
pool_identity_sources_report_derived_components test to construct a fully
explicit pool with an Explicit cache-semantics source as well as an Explicit
routing source, then assert is_derived() is false for that pool. Preserve the
existing derived-source assertion using DefaultDerived cache semantics and
routing.
In `@lib/llm/src/kv_dc_relay/resolution.rs`:
- Around line 50-55: Gate the PoolBinding and EndpointLocator types, along with
their test-only constructors and accessor methods, behind #[cfg(test)] in the
relay resolution module. Keep production relay code unchanged and ensure these
helpers remain available to tests without dead-code warnings.
---
Nitpick comments:
In `@lib/kv-router/src/indexer/cuckoo/dc.rs`:
- Around line 828-836: The source-keyed rewrite still exposes worker terminology
and an ambiguous member_counts view. In lib/kv-router/src/indexer/cuckoo/dc.rs
lines 828-836, rename the store parameter and corresponding remove/remove_member
parameters from worker to source, updating their uses to match remove_source and
source_counts; in lines 793-801, document member_counts as the legacy unscoped
view or fold duplicate entries caused by contributors sharing a worker/rank.
In `@lib/llm/src/kv_dc_relay/discovery.rs`:
- Around line 841-866: Update list_queries and open_query_streams to initiate
discovery operations for all queries concurrently using
futures::future::try_join_all rather than awaiting each request inside the loop.
Preserve error propagation and aggregate the returned instances or
DiscoveryStream values into the existing result types.
- Around line 1181-1208: Add a test covering the ambiguous-name Binding conflict
path in the discovery logic around the existing alias tests: create two cards
where an alias on one card matches the canonical model name on the other, apply
both registrations, and assert the shared request-facing name produces
MembershipConflict::Binding with the claim dropped. Keep the expectations
aligned with validate_registrations in pool_registry.rs.
- Around line 483-503: The conflict handling around worker_domain_ids must
preserve diagnostics when worker_bases is empty instead of silently dropping the
adapter. Update the branch using worker_domain_ids, ambiguous_workers, and
conflicting_worker_facts so the no-backing-base case records a
MembershipConflict::Card for an appropriate endpoint domain (or otherwise emits
the requested debug diagnostic), while retaining existing conflict behavior for
populated domain IDs.
In `@lib/llm/src/kv_dc_relay/host.rs`:
- Around line 745-759: The registration refresh error path in the loop around
pools.replace_registrations must avoid warning on every SlotInput/Health
iteration. Add persistent failure tracking for this refresh, following the
existing start_failures pattern near the relevant host logic: log the first
failure at warn, subsequent identical failures at debug, and reset the tracking
after a successful replacement.
In `@lib/llm/src/kv_dc_relay/pool_registry.rs`:
- Around line 592-601: The publication_hubs field updated by
refresh_publication_hubs is currently unused; either mark it explicitly as
intentionally staged, matching the existing PoolCatalog convention, or remove
publication_hubs, refresh_publication_hubs, and their mutation/cleanup calls
until a consumer is implemented. Prefer removing the unused sorting and hub
cloning if no near-term consumer is required.
- Around line 808-840: Update validate_registrations to avoid traversing every
pool, contributor, and existing registration for each incoming registration.
Maintain and reuse a HashMap<CanonicalModelId, ModelTarget> alongside
canonical_refcounts in PoolRegistryState, and perform existing-registration
target conflict checks through that index in O(1) per registration while
preserving duplicate and conflict errors for both the current attachment and
prior registrations.
In `@lib/llm/src/kv_dc_relay/publication_hub.rs`:
- Around line 163-196: Make PublicationHub::subscribe and the test-only snapshot
method synchronous by removing async from their signatures. Update all call
sites and tests to stop awaiting these methods while preserving their existing
locking, readiness checks, and return values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 5e9ec6d4-fcbf-44ff-a4ca-859b199d25c6
📒 Files selected for processing (11)
lib/kv-router/src/indexer/cuckoo.rslib/kv-router/src/indexer/cuckoo/dc.rslib/llm/src/kv_dc_relay.rslib/llm/src/kv_dc_relay/README.mdlib/llm/src/kv_dc_relay/actor.rslib/llm/src/kv_dc_relay/discovery.rslib/llm/src/kv_dc_relay/host.rslib/llm/src/kv_dc_relay/identity.rslib/llm/src/kv_dc_relay/pool_registry.rslib/llm/src/kv_dc_relay/publication_hub.rslib/llm/src/kv_dc_relay/resolution.rs
f108671 to
f9d01ca
Compare
f9d01ca to
c0b94e9
Compare
c0b94e9 to
47e2d9f
Compare
|
🎯 Code Coverage (details) 🔗 Commit SHA: e6ec122 | Docs | Datadog PR Page | Give us feedback! |
47e2d9f to
dcbc161
Compare
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
dcbc161 to
d38882e
Compare
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
|
/ok to test 6e06e64 |
PeaBrane
left a comment
There was a problem hiding this comment.
Suggestion on keeping the producer/consumer catalog boundary minimal.
PeaBrane
left a comment
There was a problem hiding this comment.
Neutral architectural follow-up—not a blocking verdict. The common pattern in the remaining findings is that PoolRegistry state currently serves both as local actor ownership and as externally visible “active catalog” truth. A useful invariant would be: every advertised descriptor names one unique, currently active producer generation. Concretely, give each Relay start a unique incarnation, publish or refresh catalog state only after validating the binding transition, and withdraw a generation before draining it. Keep consumer routing indexes derived from the pool-scoped catalog as discussed separately. This adds explicit activation/withdrawal steps, but centralizes lifecycle consistency now, before consumers depend on the contract.
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
|
/ok to test 6ab1c26 |
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
Signed-off-by: Nikita Sukharev <kaonael@gmail.com>
|
/ok to test e6ec122 |
Summary
This PR formalizes the Dynamo-local pool model and per-pool runtime ownership for the KV DC Relay.
The central invariant is:
Independent endpoints or deployments remain separate pools even when they serve the same canonical model. A canonical model may therefore appear in multiple pool descriptors.
Comparing those pools and selecting between them remains consumer or global-router policy.
Details
Pool discovery and model projection
ModelDeploymentCards into endpoint-centric pool memberships.IndexerDomainId;PoolIdfrom the resolved indexer domain and DC identity.PoolIdownership as fail-closed materialization conflicts.Pool runtime and producer contract
PoolRegistryas the owner of active pool actors, layout generations, reservations, and catalog publication.DcPoolCatalogandDcPoolDescriptor.ProducerIdentity, and normalized base/LoRA registrations for each active pool.KvDcRelay::startRelay incarnation.spawn_blocking, outside the registry mutex.incomplete pools.
Lifecycle, recovery, and fault handling
(worker_id, dp_rank)identifying a source andSourceEpochfencing source replacement.DcPoolCatalogbefore waiting for recovery and actor teardown.(worker_id, dp_rank), retains the newest epoch and strongest action, and keeps the pending state bounded.Out of scope
This PR intentionally does not add:
Where should the reviewer start?
components/src/dynamo/kv_dc_relay/README.mdlib/llm/src/kv_dc_relay/identity.rslib/llm/src/kv_dc_relay/discovery.rslib/llm/src/kv_dc_relay/pool_registry.rslib/llm/src/kv_dc_relay/host.rslib/llm/src/kv_dc_relay/actor.rsandlib/llm/src/discovery/kv_source_membership.rsValidation
cargo fmt --all -- --checkcargo test -p dynamo-llm kv_dc_relay:: --libcargo test -p dynamo-llm --features ckf-diagnostics kv_dc_relay:: --libcargo test -p dynamo-llm kv_source_membership::tests --libcargo clippy -p dynamo-llm --all-targets --features ckf-diagnostics -- -D warningsgit diff --check origin/main...HEADRelated Issues
Summary by CodeRabbit
New Features
Bug Fixes
Documentation