fix(dash): give instances a real Ready status instead of a Running catch-all - #106
Conversation
…tch-all InstanceStatus had no Ready state, so merge_instance hardcoded every discovered instance to Running regardless of whether it had actually passed a readiness probe. Managed services with a "ready" record status, Lemonade endpoints (built only after a health probe), and Docker containers all collapsed into the same Running label, hiding real lifecycle differences from the TUI and any future readiness-aware consumer. Add InstanceStatus::Ready, thread a status field through DiscoveredService so each discovery source reports what it actually knows, and drop merge_instance's hardcoded override in favor of svc.status. Managed-service records now map status strings (including "recovering", previously dropped from the scrapeable set entirely) through to Ready/Running/Starting. Lemonade endpoints are built as Ready since every call site only constructs them post-health-probe. Docker-discovered instances have no status source of their own, so they stay Starting until their first successful vLLM Prometheus (or Lemonade stats) scrape promotes them to Ready. Add an is_serving() helper (Ready | Running) for TUI call sites that only care about "is this actively serving" rather than the precise state, and add Ready arms to the exhaustive status_meta/status_role matches in the instances tab. Relates to EAI-7350 Signed-off-by: Michael Roy <michael.roy@amd.com>
rominf
left a comment
There was a problem hiding this comment.
LGTM. The new Ready variant is propagated exhaustively: both match status sites in instances.rs handle it (non-wildcard), and every == InstanceStatus::Running filter across dock/launcher/home was converted to is_serving(), so Ready is never misclassified in the running-count, activity feed, or launcher check. Serde is order-independent (rename_all = "lowercase", round-trip tested), #[default] stays on Unknown, and adding recovering to the scrapeable set matches the supervisor's canonical starting/recovering → Starting semantics (effectively a latent fix — recovering services were previously skipped). Unused-import hygiene handled.
Behavior note (intended): a Docker container whose vLLM never becomes scrapeable (crash loop / wrong port / non-vLLM image) now sits as Starting and is excluded from is_serving(), instead of showing Running on discovery. More correct, but a user-visible change to what counts as "running" for the Docker path — worth a line in the description.
Approving.
|
🔴 Automated review · pr-review-watcher · 83ac66e Summary
Design review finding (worth an explicit author decision)
The CLI itself uses a stricter bar for the same concept: Neither behavior is obviously wrong (a served Prometheus scrape is real evidence of serving), but it's a divergence from the app's own liveness model. Options: (a) accept the optimistic promotion and note it as intended; or (b) only promote instances that were Non-blocking suggestions
Tradeoffs (deliberate choices, surfaced for confirmation)
Positive signals
Review-only. No approval/request-changes/merge; no changes pushed. Fan-out: 3 parallel file-scoped reviewers + orchestrator synthesis/verification; all build/test/clippy claims re-run locally at 83ac66e. |
The readiness-reason helper hand-rolled two authority parsers
(port_from_base_url / host_from_base_url) with rsplit(':'), which mangles
a bracketed IPv6 loopback: host_from_base_url("http://[::1]:8000") kept
the brackets so is_loopback_host("[::1]") was false, and
port_from_base_url("http://[::1]/v1") parsed "1]" and failed. Either
path silently disabled chat_backend_wait_reason for a legitimate local
IPv6 endpoint.
Replace both helpers with the crate's existing parse_host_port, which
already strips IPv6 brackets, defaults the port from the scheme, and is
pinned by parse_host_port_handles_bracketed_ipv6. Drop the now-redundant
per-helper unit tests (covered by the llm.rs suite) and add a
bracketed-IPv6 regression test against chat_backend_wait_reason.
Also tighten the doc comment: Running is not a hard HTTP-readiness
guarantee, and record the #106/#107 status-signal dependency in-tree so
the Starting/Stopped/Error arms are discoverable as pending until that
work lands beneath this change.
Addresses pr-review-watcher blocking finding #2 (IPv6) and the doc /
test-coverage non-blocking notes on #108.
Signed-off-by: Michael Roy <michael.roy@amd.com>
Parent PR #106 (real Ready status pipeline) merged to main as d78f28f. Merge main back into the startup-phase branch, resolving the InstanceStatus conflicts by composing both changes: - Keep #107's protocol-v2 migration: InstanceStatus::Starting carries { phase: Option<StartupPhase> }, the phase-aware label(), the phase-aware registry mapping (instance_status_for_record), and PROTOCOL_VERSION = 2. - Keep #106's Ready pipeline: Ready variant, is_serving() = Ready|Running, and the Starting -> Ready promotion on first successful scrape (runner.rs), adapted to matches!(.., Starting { .. }). No catch-all downgrade: a running record with an unrecognized phase token stays Running/serving. No lost serde compatibility: the cross-crate startup_phase contract test and legacy NDJSON back-compat tests pass. Conflicts resolved: docker.rs, metrics.rs, registry.rs, runner.rs, instances.rs. Signed-off-by: Michael Roy <michael.roy@amd.com>
Summary
InstanceStatushad noReadystate, somerge_instancehardcoded every discovered instance toRunningregardless of whether it had actually passed a readiness probe. Managed services with a"ready"record status, Lemonade endpoints (only ever built after a health probe), and Docker-discovered containers all collapsed into the sameRunninglabel, hiding real lifecycle differences from the TUI and any future readiness-aware consumer (e.g. chat endpoint selection).Root Cause
rocm_dash_core::traits::merge_instanceunconditionally setstatus: InstanceStatus::Runningon everyInstanceit built, andInstanceStatusitself had no variant to represent "passed its readiness probe" as distinct from "process is up." Every discovery source (managed-service registry, Lemonade collector, Docker collector) therefore reported the same status label no matter what it actually knew about the endpoint's readiness.Changes
InstanceStatus::Readyand anis_serving()helper (Ready | Running) for call sites that only care about "is this actively serving."statusfield toDiscoveredServiceso each discovery source reports what it actually knows;merge_instancenow usessvc.statusinstead of a hardcodedRunning.rocm-dash-daemon::registry::discovered_from_recordmaps a managed-service record's status string (ready/running/starting/recovering) to the matchingInstanceStatus."recovering"was previously dropped from the scrapeable set entirely (a bug relative toapps/rocm/src/main.rs's own "is this service live" checks, which treatrecoveringas live) — it's now scraped and mapped toStarting.rocm-dash-collectors::lemonade::lemonade_servicebuilds asReadysince every call site only constructs it after a successful/api/v1/healthprobe.rocm-dash-collectors::dockerhas no readiness signal of its own, so Docker-discovered instances startStarting;rocm-dash-daemon::runner::run_looppromotes them toReadyon their first successful vLLM Prometheus scrape or Lemonade stats fetch (documented scope: this is the only readiness signal available for Docker-discovered instances).Readyarms to the exhaustivestatus_meta/status_rolematches incrates/rocm-dash-tui/src/ui/tabs/instances.rs; switch the== InstanceStatus::Runningequality checks inhome.rs/dock.rs/launcher.rsto.is_serving().Test Plan
cargo build -p rocm-dash-core -p rocm-dash-collectors -p rocm-dash-daemon -p rocm-dash-tuicargo clippy -p rocm-dash-core -p rocm-dash-collectors -p rocm-dash-daemon -p rocm-dash-tui --all-targets --all-features -- -D warningscargo test -p rocm-dash-core -p rocm-dash-collectors -p rocm-dash-daemon(all green, including 2 new tests:instance_status_ready_round_trips_as_lowercase_json,instance_status_is_serving_covers_ready_and_running_only, anddiscovered_from_record_maps_status_string_to_instance_status)cargo test -p rocm-dash-tui -- --test-threads=1(543 + 16 + 5 tests green)Relates to EAI-7350