Repository navigation
fix(operator): fail a graph while a component crash-loops - #16426
Conversation
External PR triageBlocker ( Next action: Comment Supply-chain audit of Automated triage, 2026-10-19. This comment is edited in place when the PR changes. |
sttts
left a comment
There was a problem hiding this comment.
Three inline comments on workload hierarchy dependencies, shared Pod diagnostics, and validating the design with Grove and DisaggSet before merging the DCD implementation.
73506ab to
d891155
Compare
sttts
left a comment
There was a problem hiding this comment.
Two inline comments on completing the package boundary: shared Pod controller integration in internal/controller/pods, and pure diagnosis-change detection in internal/pods. Workload ordering and graph-status policy stay with the workload programs.
183dca2 to
664e516
Compare
sttts
left a comment
There was a problem hiding this comment.
Three inline simplification suggestions: reuse controller-runtime's mapped event handler, avoid an unconstrained generic solely for status formatting, and select the first failing Pod without sorting the whole collection. These are maintainability suggestions, not additional functional bug findings.
|
@sttts Addressed your feedback. PTAL. Thanks |
|
@sttts Hey Stefan, code freeze for next release is tomorrow so would love to have this PR merged if possible. PTAL. Thanks. |
|
/ok to test 7700e29 |
|
/ok to test cbd72c6 |
✅ Dynamo PR CI passed — run 41905812821 (attempt 1) on
|
|
@sttts The two failing required checks on
|
|
@dinhxuanvu could you rebase please ? |
A DynamoGraphDeployment whose worker crash-loops stays pending indefinitely, so tools that wait on its state, such as an Argo CD health check, cannot tell a crash loop from a slow start and wait out their own timeout. The DCD controller sets Available=False with reason ContainerCrashLooping when a not-Ready Pod of the current template has a container that restarted at least twice and is down after a non-zero exit. The DGD projects that reason into state failed with reason component_crash_looping, also during a rolling update. Reading Pods and ReplicaSets needs a new RBAC grant on replicasets. Signed-off-by: Vu Dinh <vudinh@outlook.com>
Signed-off-by: Vu Dinh <vudinh@outlook.com>
Resolving the Deployment's current ReplicaSet needed extra RBAC and missed crash loops while that ReplicaSet was not yet cached. Select Pods by the Deployment's labels instead. Pod-level diagnosis is in internal/pods so other workload programs can share it. Terminating Pods are ignored; Pods of an older template still count during a rollout. Signed-off-by: Vu Dinh <vudinh@outlook.com>
Signed-off-by: Vu Dinh <vudinh@outlook.com>
…tends A crash-looping Grove component left the graph pending. So did a frontend that DisaggregatedSet lowers to a DCD. The Grove program now diagnoses each component's labeled Pods, woken by a Pod watch. The DisaggregatedSet program applies the DCD crash-loop condition. Signed-off-by: Vu Dinh <vudinh@outlook.com>
…lookups The Pod index, the queries that use it and the crash-loop watch were spread over three controller files. The Deployment lookup scanned every cached Pod in the namespace. They now live in internal/controller/pods, and both lookups go through an index. A relabeled Pod wakes its old and new owner. internal/pods gains CrashLoopChanged so the watch and the query agree on when a diagnosis changes. The reference pages state that DisaggregatedSet workers and LeaderWorkerSet-backed components are not covered. Signed-off-by: Vu Dinh <vudinh@outlook.com>
Deleting a healthy Pod, relabeling an unowned one or a Grove Pod's crash loop woke a reconcile that could not change any diagnosis. Grove Pods carry the DCD selector label, so their changes enqueued DCDs that do not exist. The crash-loop diagnosis now formats directly into the graph status. The new code and tests follow the operator's comment and test-story rules. Signed-off-by: Vu Dinh <vudinh@outlook.com>
EnqueueOwners wraps controller-runtime's mapped handler, which already enqueues both the old and new owner of an updated Pod. FindCrashLoop finds the crash-looping Pod with the smallest name in one pass without sorting its input. failCrashLoopingComponents takes diagnosis strings. Signed-off-by: Vu Dinh <vudinh@outlook.com>
cbd72c6 to
dd34515
Compare
|
@julienmancuso Done! |
|
@julienmancuso Would you mind retriggering the test suite? Thanks! |
|
/ok to test dd34515 |
Fourth upstream merge for the split v2 PR: the five commits past 02331f0. Four conflicts, all from ai-dynamo#18009, placed in the owner crates; upstream behavior whole, the stack's moves intact, no shims. - ai-dynamo#18009 keep prepared rows when ranks are past the reject thresholds: is_worker_full(dp_size)/skips_full_workers in the kernel's filter.rs; the actor keeps prepared rows and the kernel's select_with_policy drops the rows of workers whose every rank is full before the policy adopts them (PreparedRows::retain in worker-selection; the policy recounts the best cached prefix from the rows it adopts and reports whether any is left); prepared rows carry dp_size (u16, so the 128-byte row holds) set in the kernel's CandidatePreparerExt::prepare; the three new kernel queue tests and the relocated test helpers updated. - ai-dynamo#18108 CRTC decode stores appended at the leaf tail under one lock: lib/router/kv-indexer concurrent_radix_tree_compressed (auto-merged). - ai-dynamo#16258 vLLM backend defaults for omitted token limits: lib/llm preprocessor, lib/sidecar/vllm, components, docs, tests (auto-merged). - ai-dynamo#18152 planner ARIMA forecasts, ai-dynamo#16426 operator crash-loop: outside the router (auto-merged). Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Overview:
A graph whose component keeps crashing on start reports
pendingforever. Anything waiting on its state (Argo CD health checks, CI waits) can't tell a crash loop from a slow start and only gives up at its own timeout. This reportsfailedinstead, naming the crashing Pod, container and exit code.Details:
internal/podsdecides whether a Pod is crash-looping: a not-Ready Pod, neither finished nor being deleted, with a container that restarted at least twice and is down after a non-zero exit.CrashLoopChangedcompares two observations of a Pod.internal/controller/podsfinds a workload's Pods through indexes on the operator's Pod cache and decides which Pod events wake a reconcile.Available=Falsewith reasonContainerCrashLoopingfrom the Pods its Deployment selects. The DGD reportsfailedwith reasoncomponent_crash_looping.Not covered: DisaggregatedSet workers and LeaderWorkerSet-backed DCDs. LeaderWorkerSet recreates the whole group at the first container restart, so no container reaches two restarts. Detecting repeated group recreation is a follow-up that depends on LeaderWorkerSet's recovery accounting.
Validation:
Unit tests and envtests pass for
internal/pods,internal/controller/pods,internal/controller(including the DisaggregatedSet envtests) andinternal/podcache. golangci-lint v2.15.0 reports no issues.Informer-cache tests on an envtest API server: a crash-looping Pod created before the cache starts is reported once the cache syncs, and again by a rebuilt cache. Back-to-back status updates converge to the latest state.
Benchmark through a real informer cache, one namespace, unrelated Pods spread over many components (Apple M4 Pro):
Kind cluster with Grove v0.1.0-alpha.15 and LWS v0.10.0: Grove, DisaggregatedSet-frontend and Deployment-backed graphs report
failedwithin 15s of a crash loop and recover within 5s of the fix. The Grove graph staysfailedacross an operator restart, and its message follows the restart count.Where should the reviewer start?
deploy/operator/internal/pods/crashloop.go, thendeploy/operator/internal/controller/pods/.Related Issues
Summary by CodeRabbit