Repository navigation
feat(global-router): take MCS pool models from registrations, not a file - #17282
atchernych wants to merge 2 commits into
Conversation
An alternative to #17276. In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, found through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. A pool that only one side lists for two lease durations is logged once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
- A shared registration with no time left was still adopted, so a pool whose lease had just ended could be listed again until the next sweep. Such copies are now ignored. - --peer-service was silently ignored with --store memory. It is now rejected. - The warning for a pool that MCS lists without a registration now also points at registration sharing, and each instance logs how many other instances it reaches whenever that number changes. The 15 tests this PR added become 7 that cover the same cases. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
❌ Dynamo PR CI failed — run 40246321389 (attempt 1) on
|
| Other | Jobs |
|---|---|
| dynamo-runtime | ❌ 3 ✅ 5 |
Failure details
3 jobs failed: on both amd64 and arm64, test_examples_default_to_v1beta1 fails because it parses the Helm template examples/global-router/cdk/helm/dynamo-mocker/templates/dgd.yaml as plain YAML. rust-gpu was killed with exit code 137 (SIGKILL, likely OOM or a lost runner) while compiling, so it looks like an infrastructure failure.
❌ dynamo-runtime / test / sequential cuda13.0, amd64: yaml ScannerError in test_examples_default_to_v1beta1
Job: dynamo-runtime / test / sequential cuda13.0, amd64 · Failed step: Run CPU-only tests (parallelized) · Logs: gh run view --job 121332266048 -R ai-dynamo/dynamo --log-failed
manifest_path = PosixPath('/workspace/examples/global-router/cdk/helm/dynamo-mocker/templates/dgd.yaml')
E yaml.scanner.ScannerError: while scanning a simple key
E in "<unicode string>", line 28, column 5:
E {{- if .Values.runtimeVersionOve ...
E ^
E could not find expected ':'
E in "<unicode string>", line 29, column 5:
E runtimeVersionOverride: {{ .Valu ...
FAILED deploy/utils/tests/test_convert_api_version.py::test_examples_default_to_v1beta1 - yaml.scanner.ScannerError: while scanning a simple key
== 1 failed, 5519 passed, 153 skipped, 2593 deselected in 1012.63s (0:16:52) ===
deploy/utils/tests/test_convert_api_version.py::test_examples_default_to_v1beta1 scans example manifests under examples/. One of them is the Helm template examples/global-router/cdk/helm/dynamo-mocker/templates/dgd.yaml, and its {{- if .Values.runtimeVersionOverride }} directive (line 28) is not valid YAML, so the parse fails. The test failed on all 3 auto-retries too.
❌ dynamo-runtime / test / sequential cuda13.0, arm64: same yaml ScannerError in test_examples_default_to_v1beta1
Job: dynamo-runtime / test / sequential cuda13.0, arm64 · Failed step: Run CPU-only tests (parallelized) · Logs: gh run view --job 121332266106 -R ai-dynamo/dynamo --log-failed
FAILED deploy/utils/tests/test_convert_api_version.py::test_examples_default_to_v1beta1 - yaml.scanner.ScannerError: while scanning a simple key
in "<unicode string>", line 28, column 5:
{{- if .Values.runtimeVersionOve ...
^
== 1 failed, 5519 passed, 153 skipped, 2593 deselected in 1202.87s (0:20:02) ===
Same failure as on amd64: the same test fails on the same Helm template file.
❌ dynamo-runtime / rust-gpu: exit code 137 (killed) during cargo build
Job: dynamo-runtime / rust-gpu · Failed step: Run Rust checks (block-manager + media-ffmpeg + MM routing + integration tests) · Logs: gh run view --job 121332265945 -R ai-dynamo/dynamo --log-failed
Compiling dynamo-bench v1.6.0 (/__w/dynamo/dynamo/lib/bench)
Compiling dynamo-mocker v1.6.0 (/__w/dynamo/dynamo/lib/mocker)
##[error]Error: failed to run script step: Error: command terminated with non-zero exit code: command terminated with exit code 137
##[error]Process completed with exit code 1.
##[error]Executing the custom container implementation failed. Please contact your self hosted runner administrator.
##[error]Error: Internal error occurred: ... failed to create exec "...": task 0af513c5... not found
The build was SIGKILLed (exit code 137) while compiling crates. There is no compiler error, and afterwards the runner container was gone (task ... not found). This looks like an infrastructure failure (OOM kill or lost runner pod), so rerunning the failed jobs is likely enough.
For agents
{"pr": 17282, "run_id": 40246321389, "run_attempt": 1, "head_sha": "c1a4c16956accc08d56eea1b4c23dd1ba65cc45d",
"failures": [
{"job": "dynamo-runtime / test / sequential cuda13.0, amd64", "job_id": 121332266048, "failed_step": "Run CPU-only tests (parallelized)", "signature": "yaml.scanner.ScannerError parsing Helm template examples/global-router/cdk/helm/dynamo-mocker/templates/dgd.yaml", "tests": ["deploy/utils/tests/test_convert_api_version.py::test_examples_default_to_v1beta1"], "log_cmd": "gh run view --job 121332266048 -R ai-dynamo/dynamo --log-failed"},
{"job": "dynamo-runtime / test / sequential cuda13.0, arm64", "job_id": 121332266106, "failed_step": "Run CPU-only tests (parallelized)", "signature": "yaml.scanner.ScannerError parsing Helm template examples/global-router/cdk/helm/dynamo-mocker/templates/dgd.yaml", "tests": ["deploy/utils/tests/test_convert_api_version.py::test_examples_default_to_v1beta1"], "log_cmd": "gh run view --job 121332266106 -R ai-dynamo/dynamo --log-failed"},
{"job": "dynamo-runtime / rust-gpu", "job_id": 121332265945, "failed_step": "Run Rust checks (block-manager + media-ffmpeg + MM routing + integration tests)", "signature": "exit code 137 (killed) during cargo build; runner container lost", "tests": [], "log_cmd": "gh run view --job 121332265945 -R ai-dynamo/dynamo --log-failed"}
]}Posted automatically by Devin for run 40246321389. Updated on every full-CI run of this PR.
In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, finding them through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. Sharing goes through a RegistrationExchange contract, which the memory store implements. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. Instances ask each other over plain HTTP, so --peer-service is refused with --tls-cert-path for now. A pool that only one side lists for two lease durations is logged once. This replaces #17282, which targeted the global-router-v2 test branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, finding them through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. Sharing goes through a RegistrationExchange contract, which the memory store implements. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. Instances ask each other over plain HTTP, so --peer-service is refused with --tls-cert-path for now. A pool that only one side lists for two lease durations is logged once. This replaces #17282, which targeted the global-router-v2 test branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, finding them through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. Sharing goes through a RegistrationExchange contract, which the memory store implements. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. Instances ask each other over plain HTTP, so --peer-service is refused with --tls-cert-path for now. A pool that only one side lists for two lease durations is logged once. This replaces #17282, which targeted the global-router-v2 test branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, finding them through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. Sharing goes through a RegistrationExchange contract, which the memory store implements. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. Instances ask each other over plain HTTP, so --peer-service is refused with --tls-cert-path for now. A pool that only one side lists for two lease durations is logged once. This replaces #17282, which targeted the global-router-v2 test branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
In MCS mode the Global Discovery Service now accepts POST /pools, and lists a pool only while MCS shows a Ready PoolRelay for it and the pool's registration is live. The registration gives the model, capabilities, capacity and location. MCS gives the Relay and frontend addresses; the frontends come from the `<dgd>-frontend` Service, as in #17276. Instances share registrations: each polls the others' GET /registrations twice per heartbeat interval, finding them through the service's own MCS export (--peer-service), and keeps copies for the time they have left. Only registrations a PoolRelay made locally are shared, so copies are never passed on. Sharing goes through a RegistrationExchange contract, which the memory store implements. A new instance is ready only after it has asked the others once, and with --peer-service the lease must be more than two heartbeat intervals. Instances ask each other over plain HTTP, so --peer-service is refused with --tls-cert-path for now. A pool that only one side lists for two lease durations is logged once. This replaces #17282, which targeted the global-router-v2 test branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
Summary
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-configfile. This PR gets it from the PoolRelay itself, throughPOST /pools, as the design says.How it works
POST /poolsnow works (it returned 501). Each call is the pool's heartbeat, as in memory mode.<dgd>-frontendService, the same way as feat(global-router): resolve MCS pool models and live frontends #17276. The registration gives the model, capabilities, capacity and location.GET /registrations) twice per heartbeat. It finds them through MCS, the way PoolRelays find the routers (--peer-service). So every region lists every pool, and a copy lapses with its original. A new instance is ready only after it has asked the others once, so it never serves a partial list.Problems with the file in #17276
"capabilities": []and"capacity": []. In feat(global-router): take MCS pool models from registrations, not a file #17282, both come from the relay'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.POST /poolsstill 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.ready_addressesinhub.rs).What this PR adds
site_idisn't the pool's MCS cluster name. Today such a pool is silently missing.Trade-offs
POST /pools, and no PoolRelay does yet. Until then, register a pool withcurl, as feat(global-router): route round robin over pools from discovery #17273 did.--peer-service, the lease must be more than twice the heartbeat interval, so copies stay alive between refreshes. The defaults (15 s and 5 s) are fine.GET /poolsnow sayscomplete: false, because registrations are learned, not read from a shared store. The Global Router only logs this.To deploy, export the discovery service's own Service through MCS, like the router's, and pass
--peer-service <name>. Each pool also exports<dgd>-frontend, as in #17276.Validation
cargo test -p dynamo-global-discovery-service: 50 passed, 7 of them new. They cover:cargo test -p dynamo-global-ingress -p dynamo-global-router -p dynamo-global-discovery-client: all pass, including the round-robin test against the real service.cargo clippy --all-targets -- -D warningsandcargo fmton the crate: clean.🤖 Generated with Claude Code