Repository navigation
feat(global-router): keep registrations in DynamoDB - #17644
atchernych wants to merge 9 commits into
Conversation
`--registration-storage dynamodb` keeps registrations in one DynamoDB table that every service instance reads, such as a Global Table with a replica in each region. Instances in every cluster then share registrations without asking each other, as #17637 does over HTTP, and a restarted instance lists every live pool at once. - Each instance reads the table every second, with strongly consistent reads of its region's replica, and lists the registrations whose leases have not ended. A failed read keeps the last list, `Estimated`. - The table has `pk` and `sk` string keys, like the MCS registry's table in #17615. Each pool is one item with its registration, the end of its lease, and an `expires_at` time to live. - The AWS SDK is behind a `dynamodb` Cargo feature, off by default as in lib/kvbm-engine, so workspace builds skip it. The image turns it on, and CI runs the feature's tests and clippy. - `--dynamodb-table`, `--dynamodb-region` and `--dynamodb-endpoint-url` work only with `--registration-storage dynamodb`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
- A registration starts the refresh only after its write, so that the first read already lists it. - The refresh sorts what it read before it takes the view lock. - The DynamoDB Local test is ignored unless asked for, and fails without DYNAMODB_LOCAL_URL, instead of passing in CI without running. - The DynamoDB flag test checks an empty table and a bad endpoint URL separately, each with its error kind. The empty-table case passed the flag twice, so clap refused it before the value check ran. - `RegistrationStorage::DynamoDb` is cased like `DynamoDbPoolStore`. - Docs: when `complete` is false with DynamoDB storage, what an item holds, and the refresh timing while reads fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
❌ Dynamo PR CI failed — run 41146616385 (attempt 1) on
|
| Other | Jobs |
|---|---|
| Operator Integration | ❌ 1 |
Failure details
1 job failed: the operator cluster tests fail in two packages — TestClusterComponentHistoryBlocksWorkloads is rejected by the DynamoComponentDeployment admission webhook, and every TestCluster* test in internal/controller fails at clusterenv startup with a CRD update conflict.
❌ Operator Integration: DCD webhook "spec.podTemplate.spec.containers: Required value" + CRD conversion patch conflict
Job: Operator Integration · Failed step: Run operator integration tests · Logs: gh run view --job 124376032497 -R ai-dynamo/dynamo --log-failed
history_clustertest_test.go:194:
Error: Received unexpected error:
create DynamoComponentDeployment worker: admission webhook "vdynamocomponentdeployment.kb.io" denied the request: DynamoComponentDeployment.nvidia.com "worker" is invalid: spec.podTemplate.spec.containers: Required value: is required
--- FAIL: TestClusterComponentHistoryBlocksWorkloads (1.78s)
FAIL github.com/ai-dynamo/dynamo/deploy/operator/internal/testing/clusterenv 12.053s
...
env.go:108: start clusterenv: point conversion CRD "dynamocomponentdeployments.nvidia.com" at cluster-test webhook: Operation cannot be fulfilled on customresourcedefinitions.apiextensions.k8s.io "dynamocomponentdeployments.nvidia.com": the object has been modified; please apply your changes to the latest version and try again
--- FAIL: TestClusterDynamoComponentDeploymentManifests (10.69s)
--- FAIL: TestClusterDynamoGraphDeploymentManifests (0.00s)
--- FAIL: TestClusterDynamoGraphDeploymentRequestProfilesAndCreatesWorkloadManifests (0.00s)
--- FAIL: TestClusterDynamoGraphDeploymentRequestV1beta2ProducesCandidates (0.00s)
--- FAIL: TestClusterFrontendDependencyStartup (0.00s)
--- FAIL: TestClusterGroveScalerUsesScaleSubresource (0.00s)
FAIL github.com/ai-dynamo/dynamo/deploy/operator/internal/controller 10.744s
make[1]: *** [Makefile:375: clustertest] Error 1
In internal/testing/clusterenv, TestClusterComponentHistoryBlocksWorkloads (history_clustertest_test.go:194) cannot create the worker DynamoComponentDeployment because the validating webhook requires spec.podTemplate.spec.containers. In internal/controller, all 6 TestCluster* tests (13 subtests) fail in clusterenv setup (env.go:108) with a resourceVersion conflict while patching the dynamocomponentdeployments.nvidia.com CRD's conversion webhook; the first subtest fails after 10.7s and the remaining ones fail immediately with the same error.
For agents
{"pr": 17644, "run_id": 41146616385, "run_attempt": 1, "head_sha": "5b8c68d092e10a9a36602242867c717628fb612b",
"failures": [{"job": "Operator Integration", "job_id": 124376032497, "failed_step": "Run operator integration tests", "signature": "DCD admission webhook: spec.podTemplate.spec.containers Required; clusterenv CRD conversion patch conflict (object has been modified)", "tests": ["deploy/operator/internal/testing/clusterenv::TestClusterComponentHistoryBlocksWorkloads", "deploy/operator/internal/controller::TestClusterDynamoComponentDeploymentManifests", "deploy/operator/internal/controller::TestClusterDynamoGraphDeploymentManifests", "deploy/operator/internal/controller::TestClusterDynamoGraphDeploymentRequestProfilesAndCreatesWorkloadManifests", "deploy/operator/internal/controller::TestClusterDynamoGraphDeploymentRequestV1beta2ProducesCandidates", "deploy/operator/internal/controller::TestClusterFrontendDependencyStartup", "deploy/operator/internal/controller::TestClusterGroveScalerUsesScaleSubresource"], "log_cmd": "gh run view --job 124376032497 -R ai-dynamo/dynamo --log-failed"}]}Posted automatically by Devin for run 41146616385. Updated on every full-CI run of this PR.
…h/discovery-dynamodb-storage Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
…h/discovery-dynamodb-storage Brings in the 64 KiB limit on request bodies, so a registration that reaches DynamoDB is at most 64 KiB, and the readiness note in the module doc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Summary
Adds
--registration-storage dynamodb: registrations kept in one DynamoDB table that every discovery instance reads, such as a Global Table with a replica in each region. Instances in every cluster then share registrations without asking each other, and a restarted instance lists every live pool at once.Stacked on #17332. It is the alternative to #17637, which shares registrations between instances over HTTP: use this where DynamoDB is available, and #17637 (or a single instance) where it is not. If both land,
--peer-servicewithdynamodbshould be refused.Part of #17600, the pool discovery DEP.
How it works
POST /poolswrites the registration with the end of its lease, from the writing instance's clock. The pool is listed from the next read of the table, within a second.GET /poolssayscomplete: trueas soon as one read succeeds, so there is no one-lease wait after a restart. A failed read keeps the last list, withcomplete: false, until reads work again. A registration needs the table, soPOST /poolsanswers503while the table cannot be reached.--pool-discoveryvalue. Withkarmadaormcs, the join from feat(global-router): read pool models from registrations, not a file #17332 reads it in place of the memory store.The table
The same key layout as the MCS registry's table in #17615:
pkandskstrings, theexpires_attime to live, and%and#escaped in keys.pkisPOOL,skis<cluster_id>#<namespace>#<dgd_name>,registrationholds the JSON the PoolRelay sent,lease_ends_msthe end of its lease, andexpires_ata time an hour later, so that DynamoDB deletes pools that stopped registering.aws dynamodb create-tableandupdate-time-to-livecommands. It needsdynamodb:PutItemanddynamodb:Queryon the table, and AWS credentials from the usual chain, such as IRSA on EKS.Flags
--registration-storage dynamodb, with--dynamodb-table(required),--dynamodb-region(otherwise from the AWS environment) and--dynamodb-endpoint-url(for DynamoDB Local). The three fail at startup with another storage, as the other mode-specific flags do.Build
dynamodbCargo feature, off by default so that workspace builds skip it. Its dependencies are set up as inlib/kvbm-engine'ss3feature.aws-sdk-dynamodbis 1.105.0, the newest that fits the AWS crates already inCargo.lock, which only gains that one package.--features dynamodb. A binary built without it refuses--registration-storage dynamodbat startup, with a message that says how to build it.pre-merge.ymlrun the feature's tests and clippy, as for other feature-gated crates.Validation
cargo test -p dynamo-global-discovery-service --features dynamodb: 83 passed (70 library, 13 binary), with the DynamoDB Local test ignored; without the feature, 74 (61 and 13). The new tests use a fake table. They cover:Estimatedand recovers toExact, and an unreadable table before the first read is unavailable;amazon/dynamodb-local:2.6.0, as feat(global-router): add the Multi-Cluster Services registry and controller #17615 uses): an ignored test, run with--ignoredandDYNAMODB_LOCAL_URL, creates a table, skips an item that does not parse, registers, lists, watches, and sees the pool removed when its 2 s lease ends.complete: true, and a restarted instance listed it at once, also withcomplete: true. A binary built without the feature refuses--registration-storage dynamodbwith exit code 1.cargo clippy --all-targets -- -D warningswith and without the feature,cargo fmt, andcargo docwith-D warnings: clean.503like an outage, andlistingandsortedstay inmemory.rs, which this backend imports them from.🤖 Generated with Claude Code