feat: add Prometheus metrics to Grove operator controllers#633
feat: add Prometheus metrics to Grove operator controllers#633AsadShahid04 wants to merge 2 commits into
Conversation
Introduces the grove_operator metrics package with five Prometheus counters, histograms, and gauges that expose reconcile-loop observability for all three core controllers (PodCliqueSet, PodClique, PodCliqueScalingGroup): - grove_operator_reconcile_total: per-controller reconcile count partitioned by result (success / error / requeue / requeue_after) - grove_operator_reconcile_duration_seconds: per-controller reconcile latency histogram - grove_operator_in_flight_reconciles: gauge of concurrently executing reconciles per controller - grove_operator_operation_duration_seconds: sub-operation latency histogram (reconcile_spec and reconcile_status phases) per controller - grove_operator_conflict_total: count of 409 Conflict errors per controller, helping surface concurrent-modification contention during large deployments All metrics are registered on the controller-runtime global Prometheus registry (sigs.k8s.io/controller-runtime/pkg/metrics) so they are served on the existing /metrics endpoint without additional configuration changes. The ObservedReconciler wrapper is applied via Complete() in each controller's RegisterWithManager, keeping instrumentation out of reconcile logic. Sub- operation timing uses explicit start/done function pairs around reconcileSpec and reconcileStatus calls only. Closes ai-dynamo#498 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: OpenClaw Agent <agent@openclaw.local>
|
Hi @AsadShahid04 thanks for the PR. I think there's some confusion as while you follow the original issue #498, there is a follow-up GREP issue #499 which is the one with the details and full discussion. There you can see the actual (two) metrics we agreed can be implemented. |
GREP PR ai-dynamo#499 (reviewed and approved by shayasoolin) settled on exactly two custom metrics after the reviewer noted that the four original reconcile-level metrics duplicate existing controller-runtime built-ins (controller_runtime_reconcile_total, controller_runtime_reconcile_time_ seconds, controller_runtime_active_workers) and that sub-operation timing belongs in tracing, not metrics. Agreed metric surface (≤33 series total): grove_operator_status_update_conflict_total{controller, target_kind} — 409 Conflict attribution that rest_client_requests_total cannot provide at the controller/kind level grove_operator_reconcile_errors_total{controller, error_type} — transient vs. persistent error breakdown absent from controller_runtime_reconcile_total Removed: - ObservedReconciler wrapper (reconcile_total, reconcile_duration, in_flight — all duplicate built-ins) - StartOperation / operation_duration_seconds (sub-operation timing moved to tracing per GREP guidance) - conflict_total (replaced by status_update_conflict_total with target_kind label) Wiring: - All three register.go revert to Complete(r) - Conflict metric recorded inline at Status().Update/Patch call sites - Error metric recorded at Reconcile() exit path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: OpenClaw Agent <agent@openclaw.local>
|
Thanks for pointing me to PR #499, @shayasoolin — I had based the implementation on the original issue #498 and missed that the GREP proposal had narrowed scope significantly after review. Pushed a revision (2fef9e2) that reduces to the two metrics agreed in PR #499:
Removed: |
shayasoolin
left a comment
There was a problem hiding this comment.
Thanks for iterating on this and for narrowing the PR toward the two-metric direction discussed in GREP-498. The overall shape is much closer now: no wrapper, no duplicate reconcile-level metrics, and no sub-operation histogram. I found a few implementation gaps that should be addressed before this lands. Process note: GREP-498 / PR #499 is not approved or merged yet, so there may still be requested changes later if that proposal changes before landing; the items below are based on the current proposal text and discussion, not on a finalized contract.
-
grove_operator_status_update_conflict_totalis currently recorded only in the threereconcileStatusmethods, but the controllers also write the same Grove status subresources from spec/update-progress/error-recording paths. For example,podcliqueset/reconcilespec.goupdates the generation hash and observed generation,podclique/reconcilespec.gopatches update progress and observed generation,podcliquescalinggroup/reconcilespec.godoes the same for PCSG, andcontroller/common/reconcileerrorrecorder.gopatchesLastErrors. Conflicts from these status writes would not be counted, so the metric would under-report the contention signal this proposal is trying to expose. Please record the conflict metric on all PCS/PCLQ/PCSGStatus().Update()/Status().Patch()error paths whereapierrors.IsConflict(err)is true. -
grove_operator_reconcile_errors_totalis not recorded on all reconcile error exits. Some early returns bypassRecordReconcileError, including initialGet*failures and deletion-flow failures in the reconcilers. Since this is intended to be a reconcile-loop error metric, each returned reconcile error should increment exactly oneerror_typeseries. A small helper around the final(ctrl.Result, error)return values in eachReconcilemethod may make this easier to keep consistent. -
The
error_typelabel set does not match the current GREP text in PR #499. That proposal currently describes bounded Kubernetes classes:not_found,conflict,already_exists,timeout,forbidden,invalid,rate_limited, andinternal. The implementation currently usestransient,server_error, andunknown, and does not classifyAlreadyExistsorForbidden. Please align the classifier and tests with the current GREP direction, unless the GREP changes before this implementation lands.
Summary
internal/metricspackage with fivegrove_operator_*Prometheus metrics covering reconcile count, latency, in-flight gauge, sub-operation latency, and 409 Conflict rateObservedReconcilerviaComplete()in eachRegisterWithManager— zero changes to reconcile logicreconcile_specandreconcile_statussub-phases in each controller viaStartOperation/done()pairsProblem
Grove currently has zero custom Prometheus metrics. During large-scale Dynamo inference graph deployments (e.g. 5 services × 10 replicas = 50+ PodClique Pods), operators have no visibility into reconcile duration, sub-operation latency, 409 Conflict rates, or controller saturation — making it impossible to diagnose performance degradation.
Solution
operator/internal/metrics/metrics.go— new package:ObservedReconcilerwraps anyreconcile.Reconciler; records duration, in-flight count, result label (success/error/requeue/requeue_after), and conflict count automaticallyStartOperation(controller, operation)returns adone()closure for timing named sub-operationssigs.k8s.io/controller-runtime/pkg/metrics) so they appear on the existing/metricsendpoint without any additional configurationController wiring (no logic changes):
podcliqueset/register.go:Complete(grovemetrics.NewObservedReconciler(controllerName, r))podclique/register.go: samepodcliquescalinggroup/register.go: samereconciler.go:doneSpec := grovemetrics.StartOperation(..., "reconcile_spec"); ...; doneSpec()Metrics exposed:
grove_operator_reconcile_totalcontroller,resultgrove_operator_reconcile_duration_secondscontrollergrove_operator_in_flight_reconcilescontrollergrove_operator_operation_duration_secondscontroller,operationgrove_operator_conflict_totalcontrollerTesting
go build ./...: passedgo test ./internal/metrics/... ./internal/controller/...): passed — 6 new tests covering success, error, conflict, requeue, requeue_after, and sub-operation histogram populationCloses #498