What would you like to be added:
An aggregation/count capability for Custom Resource State (CRS) metrics that can count the elements of a list-valued field, optionally grouped by the value of a sub-field, and emit the result as a single aggregated Gauge — instead of emitting one time series per list element.
Concretely, given a CR with a status array like ArgoCD's Application.status.resources[]:
status:
resources:
- kind: Deployment
name: api
status: OutOfSync
health: {status: Degraded}
- kind: Service
name: api
status: Synced
health: {status: Healthy}
# ... hundreds more
...I'd like CRS to emit, per CR, the count of elements grouped by a chosen field value:
custom_resource_argocd_app_resource_count{name="my-app", sync_status="OutOfSync"} 1
custom_resource_argocd_app_resource_count{name="my-app", sync_status="Synced"} 1
Why is this needed:
Today CRS can only emit one series per list element (a path that resolves to a list is expanded element-by-element). There is no way to count or aggregate elements. To answer a simple question like "how many resources in this Application are OutOfSync?", the only option is to:
- Materialize an intermediate metric with one time series per list element — for
Application.status.resources[] that is one series for every Kubernetes object managed by every Application (element name is effectively unbounded), across every cluster; then
- Aggregate it back down in PromQL (
count by (name) (... {sync_status="OutOfSync"})); and
- Add recording + drop rules to discard the raw high-cardinality series so it isn't stored long-term.
So a small, bounded, per-CR number requires generating and scraping tens to hundreds of thousands of high-churn series just to throw most of them away. The counting could instead happen inside KSM, where the list is already being walked, at near-zero marginal cardinality (bounded by the distinct values of the group-by field). This pattern is common well beyond ArgoCD — counting status.conditions[] by type/status, counting connector tasks[] by state, counting pods/replicas in a custom controller's status, etc.
Describe the solution you'd like:
An optional aggregation on the Gauge type when path resolves to a list: count the elements, and (optionally) produce one series per distinct value of a grouped sub-field. Rough shape:
metrics:
- name: "argocd_app_resource_count"
help: "Number of managed resources, by sync status"
each:
type: Gauge
gauge:
path: [status, resources] # resolves to a list
aggregate: count # NEW: emit count of elements instead of one metric per element
groupBy: # NEW (optional): one series per distinct value, count per group
sync_status: [status] # sub-field, relative to each element
labelsFromPath:
name: [metadata, name]
Result:
custom_resource_argocd_app_resource_count{name="my-app", sync_status="OutOfSync"} <n>
custom_resource_argocd_app_resource_count{name="my-app", sync_status="Synced"} <m>
Design notes / open questions:
- Minimum viable version: just
aggregate: count (no groupBy) to emit the total element count of a list — already useful, and the simplest to implement.
- Grouping could reuse the existing
labelsFromPath element-relative selector semantics; multiple group-by keys → one series per distinct combination.
- Backward compatible: default behavior (no
aggregate) is unchanged — still one metric per element.
- An alternative API would be a dedicated
each.type (e.g. Count) rather than an option on Gauge; whichever fits the maintainers' preferred model. A future filter/predicate (count only elements matching a condition) would be a natural follow-up but isn't required for the core use case, since groupBy already yields the per-status counts.
Additional context:
- Motivating example is exposing per-ArgoCD-Application counts of OutOfSync (and unhealthy) managed resources for drift dashboards/alerting. ArgoCD's native metrics only report app-level sync status (binary), not a per-resource count, so KSM CRS is the natural place to derive it — but the current per-element-only model makes it expensive.
- The relevant field is
argoproj.io/Application .status.resources[], where each entry carries status (Synced/OutOfSync/Unknown) and health.status.
- Related issue.
If this would be accepted as valuable, I'm happy to contribute the implementation.
What would you like to be added:
An aggregation/count capability for Custom Resource State (CRS) metrics that can count the elements of a list-valued field, optionally grouped by the value of a sub-field, and emit the result as a single aggregated
Gauge— instead of emitting one time series per list element.Concretely, given a CR with a status array like ArgoCD's
Application.status.resources[]:...I'd like CRS to emit, per CR, the count of elements grouped by a chosen field value:
Why is this needed:
Today CRS can only emit one series per list element (a
paththat resolves to a list is expanded element-by-element). There is no way to count or aggregate elements. To answer a simple question like "how many resources in this Application are OutOfSync?", the only option is to:Application.status.resources[]that is one series for every Kubernetes object managed by every Application (elementnameis effectively unbounded), across every cluster; thencount by (name) (... {sync_status="OutOfSync"})); andSo a small, bounded, per-CR number requires generating and scraping tens to hundreds of thousands of high-churn series just to throw most of them away. The counting could instead happen inside KSM, where the list is already being walked, at near-zero marginal cardinality (bounded by the distinct values of the group-by field). This pattern is common well beyond ArgoCD — counting
status.conditions[]by type/status, counting connectortasks[]by state, counting pods/replicas in a custom controller's status, etc.Describe the solution you'd like:
An optional aggregation on the
Gaugetype whenpathresolves to a list: count the elements, and (optionally) produce one series per distinct value of a grouped sub-field. Rough shape:Result:
Design notes / open questions:
aggregate: count(nogroupBy) to emit the total element count of a list — already useful, and the simplest to implement.labelsFromPathelement-relative selector semantics; multiple group-by keys → one series per distinct combination.aggregate) is unchanged — still one metric per element.each.type(e.g.Count) rather than an option onGauge; whichever fits the maintainers' preferred model. A futurefilter/predicate (count only elements matching a condition) would be a natural follow-up but isn't required for the core use case, sincegroupByalready yields the per-status counts.Additional context:
argoproj.io/Application.status.resources[], where each entry carriesstatus(Synced/OutOfSync/Unknown) andhealth.status.If this would be accepted as valuable, I'm happy to contribute the implementation.