Skip to content

feat: add nodepool_provisioning_duration metric - #40

Open
Asrarfarooq wants to merge 5 commits into
nstogner:mainfrom
Asrarfarooq:nodepool_provisioning_duration
Open

feat: add nodepool_provisioning_duration metric#40
Asrarfarooq wants to merge 5 commits into
nstogner:mainfrom
Asrarfarooq:nodepool_provisioning_duration

Conversation

@Asrarfarooq

@Asrarfarooq Asrarfarooq commented May 4, 2026

Copy link
Copy Markdown
Contributor

This PR introduces a new metric, nodepool_provisioning_duration, to track and report the time taken for GKE NodePools to become ready after creation. This helps in monitoring provisioning latency, detecting scaling issues, and capturing provisioning failures.

Results

We verified the new metric on (megamon-test-cluster) by creating test TPU node pools and observing the output.

Raw Metrics Output (Ongoing State)

Curling the metrics endpoint (:8080/metrics) while the NodePool was still provisioning confirmed that the metric is emitted with the "provisioning" state:

# HELP megamon_alpha_nodepool_provisioning_duration_seconds Time spent provisioning.
# TYPE megamon_alpha_nodepool_provisioning_duration_seconds gauge
megamon_alpha_nodepool_provisioning_duration_seconds{nodepool_name="tpu-validation-pool-1763155065",otel_scope_name="megamon",otel_scope_version="",provisioning_state="provisioning",tpu_accelerator="tpu-v4-podslice",tpu_topology="2x2x1"} 319.972495761

Raw Metrics Output (Success State)

Once the NodePool entered the RUNNING status, the metric successfully transitioned to the "success" state with the final duration frozen:

# HELP megamon_alpha_nodepool_provisioning_duration_seconds Time spent provisioning.
# TYPE megamon_alpha_nodepool_provisioning_duration_seconds gauge
megamon_alpha_nodepool_provisioning_duration_seconds{nodepool_name="tpu-validation-pool-1763155065",otel_scope_name="megamon",otel_scope_version="",provisioning_state="success",tpu_accelerator="tpu-v4-podslice",tpu_topology="2x2x1"} 330.000998025

Controller Logs

The GCS-serialized event record confirmed the internal calculation of provisioningDuration (in nanoseconds) matching the external metrics:

{
  "type": "nodepools",
  "summary": {
    "downTimeProvisioned": 330000998025,
    "provisioningDuration": 330000998025,
    "provisioningState": "success"
  }
}

These results confirm that:

  • The metric is correctly registered and emitted.
  • It dynamically tracks provisioning states (provisioning, success, and failed).
  • It attaches the correct labels for topology and accelerator type.

Key Changes

  • internal/records/events.go: Added StateProvisioning, StateSuccess, and StateFailed constants to eliminate magic strings, and implemented the failed provisioning timeline calculation.
  • internal/records/report.go: Added the Failed flag to Upness to bridge data from the poller to events.
  • internal/aggregator/poller/node_poller.go: Mapped GKE "ERROR" and "RUNNING_WITH_ERROR" statuses to the internal Failed state.
  • internal/metrics/metrics.go: Registered and implemented the emission of the new nodepool_provisioning_duration metric.
  • internal/records/events_test.go: Added unit tests for ongoing and failed provisioning scenarios.
  • test/integration/cases_test.go: Added integration specs verifying both ongoing and failed provisioning scenarios, added DownTimeInitial assertions, and implemented label assertions for metrics.
  • docs/metrics.md: [NEW] Created metric documentation and examples.
  • docs/RELEASE_NOTES.md: Updated release notes for the upcoming v1.1.1 release.
  • go.mod: Resolved direct dependency alignment for sigs.k8s.io/lws.

Context

This branch has been rebased on top of the latest main (which includes the consolidated workload reconciler changes from PR #39) to ensure a clean and isolated diff containing only this feature. Conflict in events_test.go was resolved by combining assertions from both branches.

Updated internal/records/events_test.go to expect ProvisioningDuration: 1h and ProvisioningState: "success" for older test cases that calculate these values but previously defaulted to zero values in the test table.
@echiugoog

Copy link
Copy Markdown
Contributor

Can you update the release notes and document the metric definition? perhaps start a docs/metrics.md and show what's expected for nodepools still being provisioned, failed and succeeded

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.

Can you add a test case for the provisioning and failed case as well?

@Asrarfarooq Asrarfarooq Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added unit test cases for ongoing provisioning (StateProvisioning) and failed provisioning (StateFailed) inside internal/records/events_test.go.

})

// Added to test provisioning duration for resources that never become UP.
It("should correctly summarize a flow that never becomes UP", func() {

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.

can you assert the expectation of downtime_initial as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the assertion for summary.DownTimeInitial to the integration test case.

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.

Can we add in the failed provisioning case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the new integration test case It("should correctly summarize a failed provisioning flow") to cover this.

Comment thread internal/records/events.go Outdated
if n == 1 {
summary.DownTime = now.Sub(r.UpEvents[0].Timestamp)
summary.ProvisioningDuration = summary.DownTime
summary.ProvisioningState = "provisioning"

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.

lets use const for valid values

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Defined StateProvisioning, StateSuccess, and StateFailed constants in internal/records/events.go and updated the code and tests to use them.

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.

Let's also assert the metric is correctly emitted as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the WithLabel helper to the test metric structure and implemented assertions to verify nodepool_provisioning_duration_seconds is correctly emitted with the right labels during integration runs.

@echiugoog echiugoog 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.

Please also update RELEASE_NOTES.md

@Asrarfarooq

Copy link
Copy Markdown
Contributor Author

Can you update the release notes and document the metric definition? perhaps start a docs/metrics.md and show what's expected for nodepools still being provisioned, failed and succeeded

Created docs/metrics.md containing the metric definitions, labels, and raw output examples for all three states (provisioning, success, and failed). I've also updated docs/RELEASE_NOTES.md.

@Asrarfarooq

Copy link
Copy Markdown
Contributor Author

Please also update RELEASE_NOTES.md

Updated docs/RELEASE_NOTES.md with details for this feature under the version v1.1.1.

@Asrarfarooq
Asrarfarooq requested a review from echiugoog June 26, 2026 18:35
Comment thread docs/metrics.md

## Nodepool Metrics

### `megamon.nodepool.provisioning.duration`

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.

maybe add a version introduced in

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the metric introduction version (v1.1.1) to the documentation.

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.

LGTM

Comment thread docs/RELEASE_NOTES.md Outdated
## v1.1.1

### New features and improvements
* Add `nodepool.provisioning.duration` metric to track GKE NodePool provisioning latency with state tracking (`provisioning`, `success`, `failed`).

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.

can you reference the docs/metrics.md file as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a reference link pointing to the docs/metrics.md file.

Attrs: utils.ExtractNodePoolAttrs(np),
Status: np.Status,
ExpectedDown: np.Status == "STOPPING" || np.Status == "DELETING",
Failed: np.Status == "ERROR" || np.Status == "RUNNING_WITH_ERROR",

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.

Let's introduce const for these strings

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Defined constants for GKE status strings inside node_poller.go

UpEvents []UpEvent `json:"upEvents"`
}

const (

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.

can we prefix this so we know it's for nodepool provisioning?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Prefix-renamed state constants

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.

LGTM

Attrs: utils.ExtractNodePoolAttrs(np),
Status: np.Status,
ExpectedDown: np.Status == "STOPPING" || np.Status == "DELETING",
Failed: np.Status == "ERROR" || np.Status == "RUNNING_WITH_ERROR",

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.

Let's update node_poller_test.go to cover the change as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Expanded node_poller_test.go to cover these changes as well.

Comment thread internal/records/events.go Outdated

// ProvisioningDuration is the time spent provisioning.
ProvisioningDuration time.Duration `json:"provisioningDuration"`
// ProvisioningState is the state of provisioning (provisioning or success).

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.

it's not just provisioning or success? perhaps omit this part of the comment for brevity

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Omitted comment for brevity.

@Asrarfarooq
Asrarfarooq requested a review from echiugoog July 7, 2026 17:10
Comment thread docs/metrics.md

## Nodepool Metrics

### `megamon.nodepool.provisioning.duration`

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.

LGTM


var log = logf.Log.WithName("upness")

const (

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.

can you use these in node_poller_test as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added

UpEvents []UpEvent `json:"upEvents"`
}

const (

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.

LGTM

@Asrarfarooq

Copy link
Copy Markdown
Contributor Author

@nstogner could you please review this PR?

It adds the NodePool provisioning duration metric and refactors statuses and states into typed constants (per Edwin's feedback).

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