Skip to content

fix(machine-controller): escalate WaitingForMeasurements to Failed after timeout#4027

Open
wminckler wants to merge 5 commits into
NVIDIA:mainfrom
wminckler:fix/measuring-waiting-timeout
Open

fix(machine-controller): escalate WaitingForMeasurements to Failed after timeout#4027
wminckler wants to merge 5 commits into
NVIDIA:mainfrom
wminckler:fix/measuring-waiting-timeout

Conversation

@wminckler

Copy link
Copy Markdown
Contributor

A host that stops network-booting after entering HostInitializing/Measuring { WaitingForMeasurements } sits there indefinitely with no alert, no Failed transition, and nothing in the measurement journal — completely invisible to operators and fleet tooling. Healthy fleets complete this state in ~10 minutes; the bug was observed holding a host for 20+ hours. The condition is reachable in practice: 1 of 16 hosts hit it within one weekend on its 30th consecutive reprovision cycle.

The root cause is that handle_measuring_state returns MeasuringOutcome::NoChange unconditionally when MeasurementMachineState is Discovered (Scout hasn't submitted a report yet), with no timeout, no counter, and no escalation path. This affects all three call sites that share the function: HostInit/Measuring, Measuring (post-Ready re-attestation), and PostAssignedMeasuring.

This change adds a configurable waiting_for_measurements_timeout (default 4 h) to ReachabilityParams and MachineStateControllerConfig. When the timeout elapses and the DB state is still Discovered, handle_measuring_state returns Unsuccessful with the new FailureCause::MeasurementsNotReceived, causing all three call sites to transition to Failed and become visible to operators and remediation tooling.

The has_passed_attestation utility call site (which checks a just-received report, not a stuck state) passes Duration::MAX so the timeout never fires there.

Related issues

Fixes https://nvbugspro.nvidia.com/bug/6447134

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Additional Notes

The 4-hour default is intentionally generous — it is 24× the healthy completion time — so it catches stuck hosts without risk of false positives on slow but healthy fleets. Sites can tune it via machine_state_controller.waiting_for_measurements_timeout in config.

@copy-pr-bot

copy-pr-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 711dc236-1437-4a10-b055-cf97a812b342

📥 Commits

Reviewing files that changed from the base of the PR and between e0467e3 and e2c94cf.

📒 Files selected for processing (1)
  • crates/api-core/src/cfg/README.md

Summary by CodeRabbit

  • New Features
    • Added a configurable timeout for hosts in the “waiting for measurements” measuring flow.
    • Hosts that exceed the timeout are now transitioned to an unsuccessful state with a specific “MeasurementsNotReceived” failure reason.
    • Default timeout is 4 hours, configurable via MachineStateControllerConfig.
  • Bug Fixes
    • Timeout decisions now use elapsed time since the host entered the measuring state.
  • Tests
    • Updated config (de)serialization tests and test environment setup to include the new timeout field.

Walkthrough

Adds a configurable timeout for hosts waiting for measurements. The controller tracks elapsed time in measuring states and returns MeasurementsNotReceived when the timeout expires, with configuration propagated through handlers, setup, attestation handling, and test environments.

Changes

Measurement timeout handling

Layer / File(s) Summary
Failure and timeout configuration
crates/api-model/src/machine/mod.rs, crates/machine-controller/src/config/controller.rs, crates/api-core/src/cfg/README.md
Adds the MeasurementsNotReceived failure cause and introduces a serialized controller timeout with four-hour production defaults and extended test defaults.
Timeout evaluation in measuring state
crates/machine-controller/src/lib.rs
Extends handle_measuring_state with state-entry and timeout parameters, returning an unsuccessful outcome when a discovered machine exceeds the measurement wait duration.
Timeout propagation and validation
crates/machine-controller/src/handler.rs, crates/api-core/src/attestation/measured_boot.rs, crates/api-core/src/setup.rs, crates/machine-controller/tests/integration/env.rs, crates/api-core/src/tests/common/api_fixtures/mod.rs, crates/api-core/src/cfg/file.rs
Passes the configured timeout through measuring paths, attestation handling, setup, test environments, fixtures, and configuration expectations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MachineStateHandler
  participant handle_measuring_state
  participant MeasurementMachineState
  participant FailureCause
  MachineStateHandler->>handle_measuring_state: pass state entry time and timeout
  handle_measuring_state->>MeasurementMachineState: evaluate Discovered state
  handle_measuring_state->>FailureCause: create MeasurementsNotReceived after timeout
  handle_measuring_state-->>MachineStateHandler: return NoChange or Unsuccessful
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main behavioral change: timing out WaitingForMeasurements into Failed.
Description check ✅ Passed The description is directly aligned with the implemented timeout escalation, config additions, and affected call sites.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@wminckler

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@wminckler
wminckler force-pushed the fix/measuring-waiting-timeout branch from 79129dd to b17daca Compare July 23, 2026 19:40
@wminckler
wminckler marked this pull request as ready for review July 24, 2026 12:37
@wminckler
wminckler requested a review from a team as a code owner July 24, 2026 12:37
@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-07-24 12:40:23 UTC | Commit: b17daca

wminckler and others added 2 commits July 24, 2026 10:34
…ter timeout

A host that stops network-booting after the Measuring state is entered
will wait in WaitingForMeasurements indefinitely with no alert, no Failed
transition, and nothing in the measurement journal — invisible to operators
and fleet tooling. Healthy fleets complete this state in ~10 minutes.

Add a configurable `waiting_for_measurements_timeout` (default 4 h) to
`ReachabilityParams` and wire it through `MachineStateControllerConfig`
and the builder. When the timeout elapses while `MeasurementMachineState`
is still `Discovered`, `handle_measuring_state` returns
`MeasuringOutcome::Unsuccessful` with the new `FailureCause::MeasurementsNotReceived`,
causing all three Measuring call-sites (HostInit, Measuring, PostAssignedMeasuring)
to transition to `Failed` and become visible.

The `has_passed_attestation` utility call site passes `Duration::MAX` so
the timeout never fires there (it checks a just-received report).

Fixes: https://nvbugspro.nvidia.com/bug/6447134

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… to struct literals

Fills in the new ReachabilityParams and MachineStateControllerConfig field
in test fixtures and config-deserialization tests missed by the initial commit.
Also carries the carbide-health fix (Option<String> id) forward on this branch
to keep lint-police clean.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@wminckler
wminckler force-pushed the fix/measuring-waiting-timeout branch from b17daca to 5ba0062 Compare July 24, 2026 14:45
@github-actions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/api-core/src/cfg/file.rs (1)

948-969: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Do not expose unbounded per-object metrics as a Prometheus configuration option.

This configuration enables O(fleet) state series and defaults the selection to every object type. Filtering by type does not bound machine/switch/rack counts, and the existing per-object metrics pattern uses object identifiers as labels. Use bounded aggregate state metrics, or redesign this as a capped diagnostic snapshot rather than an unbounded metrics endpoint.

As per coding guidelines, **/*.{rs,proto} must keep metrics cardinality bounded and must not use highly unique labels such as machine or instance identifiers.

Also applies to: 972-988, 990-1030, 4221-4228, 4584-4591

🤖 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 `@crates/api-core/src/cfg/file.rs` around lines 948 - 969, Remove the
per-object metrics feature centered on PerObjectStateMetricsConfig and its
per_object_state_metrics field, including the dedicated endpoint, emission
logic, and related defaults or serialization paths. Do not expose machine,
switch, rack, or other object identifiers as metric labels; replace the
functionality with bounded aggregate state metrics, or convert it to a capped
diagnostic snapshot that enforces an explicit size limit.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@crates/api-core/src/cfg/file.rs`:
- Around line 948-969: Remove the per-object metrics feature centered on
PerObjectStateMetricsConfig and its per_object_state_metrics field, including
the dedicated endpoint, emission logic, and related defaults or serialization
paths. Do not expose machine, switch, rack, or other object identifiers as
metric labels; replace the functionality with bounded aggregate state metrics,
or convert it to a capped diagnostic snapshot that enforces an explicit size
limit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1baddcd0-812f-4ee9-bf89-efeb063c62c9

📥 Commits

Reviewing files that changed from the base of the PR and between b17daca and 5ba0062.

⛔ Files ignored due to path filters (1)
  • rest-api/proto/core/gen/v1/nico_nico.pb.go is excluded by !**/*.pb.go, !**/gen/**, !rest-api/**/*.pb.go
📒 Files selected for processing (9)
  • crates/api-core/src/attestation/measured_boot.rs
  • crates/api-core/src/cfg/file.rs
  • crates/api-core/src/setup.rs
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/lib.rs
  • crates/machine-controller/tests/integration/env.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/api-core/src/setup.rs
  • crates/api-core/src/attestation/measured_boot.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/src/lib.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs

…match

Rebase introduced a new metric_label() method on FailureCause that requires
exhaustive matching; add the missing arm for MeasurementsNotReceived.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@wminckler
wminckler force-pushed the fix/measuring-waiting-timeout branch from 744a563 to e0467e3 Compare July 24, 2026 14:56
wminckler and others added 2 commits July 24, 2026 11:10
CI's buf/go fmt version strips trailing alignment spaces on two lines
in DNSMessage_DNSQuestion that local tooling left intact.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… cfg/README.md

Required by every_serialized_section_key_is_documented.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants