Repository navigation
feat(reliability-dashboard): add control-plane reconciliation metrics (Hypershell 286 ) - #360
Conversation
|
Important Review skippedAuto reviews are limited based on label configuration. 🚫 Excluded labels (none allowed) (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Amber reviewStatus: Complete |
e368091 to
de86357
Compare
8b1e2a8 to
0c47f1b
Compare
0c47f1b to
5a70079
Compare
c098ba0 to
c31f40d
Compare
amber-review-bot
left a comment
There was a problem hiding this comment.
Verdict
This revision carries all fixes from the prior review into the current head: the stale gauge now serializes store/delete/count/record under staleResourcesMu, carries the hypershell.cluster_id label with max-by-cluster BFF dedup, the lag P50 is optional so an idle/fresh histogram no longer 502s the whole batch, and Go + BFF route/unit coverage now satisfies CRM-005. No new in-code defects; the only open items are cross-PR design decisions that need maintainer coordination.
Findings
No new findings this revision. The committer addressed the previous concerns (see below).
Cross-PR coordination
Two independent efforts change contracts this PR now depends on; each needs a maintainer decision plus a defined merge order.
- Control-plane cluster identity source (#362). This PR threads
cfg.ClusterIDintoNewGatewayReconciler(cmd/hypershell-controller/main.go,reconciler.go:361,416) and uses it as thehypershell.cluster_idlabel on the stale gauge (metrics.go:317-319). #362 deletes theClusterIDconfig field andHYPERSHELL_CLUSTER_IDenv entirely (internal/config/config.go) and re-sources identity from the registration-resolvedcluster_id(RegisteredClusterIDForSubject), already wiring a registeredclusterIDinto the reconcilers. If #362 lands first, this PR stops compiling oncfg.ClusterIDand the stale-gauge label must be rewired to the registered identity. Maintainers should pick the canonical identity source and sequence the two merges so the label reads it correctly. - Canonical convergence/staleness signal (#151, #200). This PR adds
SetResourceStatusStale(...)intoGatewayReconciler.Handleand defines staleness asGetReleaseId() != GetObservedReleaseId()(reconciler.go:445-446). #151 modifies the sameHandleto re-key convergence on a newgeneration/observed_generationprimitive (converged =observed_generation == generation, with the control plane writingobserved_generation), and #200 defines the canonical control-plane reconciliation contract those generations implement. Two divergent definitions of "converged/stale" would ship in the same reconciler. A maintainer should choose the canonical convergence signal, decide whether this stale metric should key on the generation model rather than release IDs, and order the merges accordingly.
Previous concerns
- [Major] Reconciliation-lag P50 could 502 the whole endpoint on idle/fresh control planes (r4098852159) - addressed. The lag P50 is the only query run with
optional = true(metrics-control-plane-reconciliation.ts:105);queryInstantreturnsundefinedfor a non-finite/invalid optional sample instead of throwing (:59-62); the lag field is omitted via...(lag === undefined ? {} : {...})(:123); andbff/test/metrics-control-plane-reconciliation.test.tsasserts the required counts survive an idleNaN/empty lag histogram. - [Major] Feature lacks the tests required by CRM-005 (r4098852165) - addressed.
internal/otel/metrics_test.go:75-124(TestReconciliationMetrics) covers the failures/retries counter types, thehypershell.reconciliation.lagexplicit bucket bounds, and the stale gauge'shypershell.cluster_idlabel plus theSetResourceStatusStalestore/store/delete dedup count;dashboard-control-plane.test.tsasserts the reconciliation mapping; and the route-level502path is now covered bybff/test/metrics-control-plane-reconciliation-route.test.ts:65-76, which drives a failing upstream and asserts502+{error: "Metrics unavailable", statusCode: 502}. - [Minor] Redundant
map()per metric inmapControlPlaneReconciliationResponse(r4098852172) - addressed. Each trend is computed once into a local (failuresTrend/retriesTrend/lagTrend/staleTrend) and reused in both the guard and the value indashboard-control-plane.ts. - [Minor] Stale gauge has no cluster label / dedup (r4105649788) - addressed. The Go gauge records
attribute.String("hypershell.cluster_id", AttentionClusterID(clusterID))(metrics.go:317-319), and the BFF query issum(max by (hypershell_cluster_id) (hypershell_stale_resource_status_count))(metrics-control-plane-reconciliation.ts:19-20), matching the sandbox attention-gauge dedup pattern. - [Minor] PR silently changes the just-merged HYPERSHELL-285 API-reliability display contract (r4105649796) - addressed. The
req/s->requests/secandtoFixed(2)->toFixed(3)change is retained but now documented as an intentional compatibility change inspecs/platform/control-plane-reconciliation-metrics.spec.md(Assumptions), instead of the spec asserting the contract is unchanged. - [Minor] Stale-gauge count+record is not atomic with the map mutation (r4105894585) - addressed.
SetResourceStatusStalenow takesstaleResourcesMu.Lock()/defer Unlock()before the store/delete, theRangecount, and theRecord, so concurrent reconciles can no longer export a snapshot that disagrees with the map (metrics.go:307-319).
The committer addressed the previous concerns.
Findings Summary (ordered by severity, highest first)
No open findings in this PR's code. Remaining items are cross-PR design decisions (canonical cluster-identity source; canonical convergence/staleness signal) that require maintainer coordination and a merge order.
Convention Checklist
| Convention | Result |
|---|---|
No panic() in production code |
Pass |
| Errors wrapped / propagated with context | Pass |
| No secrets in logs or responses | Pass |
| Input validated (static PromQL, no injection) | Pass |
| Prometheus labels bounded (resource IDs stay in process memory) | Pass |
| Authenticated BFF endpoint | Pass |
Replica-safe gauge aggregation (max-by-cluster dedup) |
Pass |
| Concurrency-safe gauge count+record | Pass |
| Go coverage for new counters/histogram/gauge/dedup | Pass |
Route-level failure (502) test coverage |
Pass |
| Spec self-consistency (API-reliability change documented) | Pass |
Amber reviewStatus: Complete |
markturansky
left a comment
There was a problem hiding this comment.
Verdict
This revision is clean: all six prior Amber findings are fixed on this head (the lag P50 no longer 502s an idle/fresh control plane, the stale gauge carries a deduped hypershell.cluster_id label recorded atomically under staleResourcesMu, and Go + BFF unit/route tests now satisfy CRM-005). No new in-code defects; the only open items are cross-PR design decisions that need maintainer coordination and a defined merge order.
Findings
No new findings this revision. The committer addressed the previous concerns (see below).
Cross-PR coordination
Three open efforts change contracts this PR now depends on; each needs a maintainer decision plus a defined merge order.
-
Control-plane cluster identity source (#362). This PR threads
cfg.ClusterIDintoNewGatewayReconciler(cmd/hypershell-controller/main.go,reconciler.go) and uses it as thehypershell.cluster_idlabel on the stale gauge (metrics.go:317-319). #362 deletes theClusterIDconfig field and theHYPERSHELL_CLUSTER_IDenv entirely frominternal/config/config.goand re-sources identity from a registration-resolvedclusterIDat startup. If #362 lands first, this PR stops compiling oncfg.ClusterIDand the stale-gauge label must be rewired to the registered identity. Maintainers should pick the canonical identity source and sequence the two merges so the label reads it correctly. -
Canonical convergence/staleness signal (#151, #200). This PR adds
SetResourceStatusStale(...)intoGatewayReconciler.Handleand defines staleness asgw.GetReleaseId() != gw.GetObservedReleaseId()(reconciler.go:445-446). #151 modifies the sameHandleto re-key convergence on a newgeneration/observed_generationprimitive (converged =observed_generation == generation), and #200 defines the canonical control-plane reconciliation contract those generations implement. Two divergent definitions of "converged/stale" would ship in the same reconciler. A maintainer should choose the canonical convergence signal, decide whether this stale metric should key on the generation model rather than release IDs, and order the merges accordingly. -
Reconciliation widget help text (#365). This PR introduces the reconciliation dashboard widgets and their metric IDs (
reconciliation-failures,reconciliation-retries,reconciliation-lag,stale-resource-status-count) plus their message catalog entries inpackages/operational-dashboard-ui/src/dashboard/reliability-dashboard-widgets.tsx,messages.ts, andcomponents/web-console/locales/en.json. #365 adds help text keyed on those same reconciliation metric IDs (dashboardHelpReconciliationFailures/Retries/Lag) and edits the samemessages.ts,en.json, anddashboard-widget.css. #365's help-text mapping presupposes this PR's widgets and metric IDs exist, and it covers only three of the four IDs (nostale-resource-status-count). Maintainers should merge this PR first, keep the metric-ID naming contract identical across both, and decide whether the stale widget also needs help text.
Previous concerns
- [Major] Reconciliation-lag P50 could 502 the whole endpoint on idle/fresh control planes (r4098852159) - addressed. The lag P50 is the only query run with
optional = true(metrics-control-plane-reconciliation.ts:105);queryInstantreturnsundefinedfor a non-finite/invalid optional sample instead of throwing (:59-62); the lag field is omitted via...(lag === undefined ? {} : {...})(:123); andbff/test/metrics-control-plane-reconciliation.test.tsasserts the required counts survive an idleNaN/empty lag histogram. - [Major] Feature lacks the tests required by CRM-005 (r4098852165) - addressed.
internal/otel/metrics_test.go(TestReconciliationMetrics, from L75) covers the failures/retries counter types, thehypershell.reconciliation.lagexplicit bucket bounds, and the stale gauge'shypershell.cluster_idlabel plus theSetResourceStatusStalestore/store/delete dedup count;dashboard-control-plane.test.tsasserts the reconciliation mapping; and the required-query502path is covered bybff/test/metrics-control-plane-reconciliation-route.test.ts:65-74, which drives a failing upstream and asserts502+{error: "Metrics unavailable", statusCode: 502}. - [Minor] Redundant
map()per metric inmapControlPlaneReconciliationResponse(r4098852172) - addressed. Each trend is computed once into a local and reused in both the guard and the value indashboard-control-plane.ts. - [Minor] Stale gauge has no cluster label / dedup (r4105649788) - addressed. The Go gauge records
attribute.String("hypershell.cluster_id", AttentionClusterID(clusterID))(metrics.go:317-319), and the BFF query issum(max by (hypershell_cluster_id) (hypershell_stale_resource_status_count))(metrics-control-plane-reconciliation.ts:19-20), matching the sandbox attention-gauge dedup pattern. - [Minor] PR silently changes the just-merged HYPERSHELL-285 API-reliability display contract (r4105649796) - addressed. The
req/s->requests/secandtoFixed(2)->toFixed(3)change is retained but now documented as an intentional compatibility change inspecs/platform/control-plane-reconciliation-metrics.spec.md(Assumptions), instead of the spec asserting the contract is unchanged. - [Minor] Stale-gauge count+record is not atomic with the map mutation (r4105894585) - addressed.
SetResourceStatusStalenow takesstaleResourcesMu.Lock()/defer Unlock()before the store/delete, theRangecount, and theRecord, so concurrent reconciles can no longer export a snapshot that disagrees with the map (metrics.go:307-319).
The committer addressed the previous concerns.
Findings Summary (ordered by severity, highest first)
No open findings in this PR's code. Remaining items are cross-PR design decisions (canonical cluster-identity source; canonical convergence/staleness signal; reconciliation widget help-text ordering) that require maintainer coordination and a merge order.
Convention Checklist
| Convention | Result |
|---|---|
No panic() in production code |
Pass |
| Errors wrapped / propagated with context | Pass |
| No secrets in logs or responses | Pass |
| Input validated (static PromQL, no injection) | Pass |
| Prometheus labels bounded (resource IDs stay in process memory) | Pass |
| Authenticated BFF endpoint | Pass |
Replica-safe gauge aggregation (max-by-cluster dedup) |
Pass |
| Concurrency-safe gauge count+record | Pass |
| Go coverage for new counters/histogram/gauge/dedup | Pass |
Route-level failure (502) test coverage |
Pass |
| Spec self-consistency (API-reliability change documented) | Pass |
c31f40d to
1ce3ce4
Compare

Summary
Breaking change? No
Tracking
Fixes HYPERSHELL-286
Specs and other PRs
Related specs and dependent PRs
specs/platform/control-plane-reconciliation-metrics.spec.md=> defines reconciliation metric types, bounded labels, PromQL, BFF response behavior, and verification requirements.specs/web-console/reliability-dashboard.spec.md=> extends the reliability dashboard contract with reconciliation sources, widgets, layouts, and failure handling.specs/platform/api-reliability-metrics.spec.md=> aligns API request-rate units and display precision used by the expanded dashboard.packages/operational-dashboard-ui/DATA_SOURCES.md=> documents the new BFF source and metric mappings.Components changed
components/control-planecomponents/web-consolepackages/operational-dashboard-uispecs/platformspecs/web-consoleChanges with impact
GET /api/metrics/control-plane-reconciliationBFF contract; required current-value query failures return502, while optional lag and historical-series failures remain partial.Verification
Screenshots / video
Questions for discussion
Details
Technical details (for humans and bots)
increasefor 24-hour and hourly counter values,histogram_quantile(0.50, ...)for lag, and a summed gauge for stale resources. Historical queries use the rolling 24-hour range with hourly samples and omit invalid/unavailable series instead of converting them to zero.