Task · GOB-0001

Don't promote service.version to a per-series metric label

Status
To Do
Priority
high
Labels
telemetry, cardinality, from-gh-165
Updated
2026-08-14

Description

Migrated from GitHub issue #165 (open at the 2026-08-14 tracker migration; full original body in archive/github-issues-2026-08-14.json).

service.version is set on the OTLP metrics resource (the MeterProvider’s Resource), so it is promoted to a service_version label on every emitted metric series. Per the OpenTelemetry → Prometheus compatibility spec that is a deviation: only service.name (+ service.namespace) → job and service.instance.idinstance are meant to become labels. Every other resource attribute — service.version included — belongs on an info metric (target_info, or a *_build_info gauge), per the OpenMetrics 1.0 convention. Promoting a resource attribute to per-series labels is a documented, non-default opt-in.

Impact: each new build mints a whole new series set. After a redeploy the old- and new-version series coexist for the query-lookback window, so any sum-style panel adds both (a transient multiplier), and active-series cardinality grows with the number of versions ever seen. Repos on stable release tags hit the cardinality growth but rarely see the doubling.

Shape of the fix: keep service.version on the OTel resource (semconv-correct, flows to target_info); stop it becoming a per-series label; rely on the existing *_build_info{version=...} gauge joined via group_left where a panel needs it.

Cross-repo consistency pass — the detailed spec citations are in rknightion/graph2otel#104. Sibling issues: tailscale2otel#187, opnsense-exporter#270.

Acceptance Criteria

Definition of Done

Implementation Notes

GitHub issue #165 was deleted on 2026-08-14 once this task took over the work — gh issue view 165 now 404s. Do not go looking for it. The full original body (2,267 bytes, no comments) is in the archive:

jq ".[] | select(.number == 165)" archive/github-issues-2026-08-14.json

It is not in the closed-issues index doc either, because that table indexes the closed set and this one was open. The archive plus this task are the whole record.

References

View the source file on GitHub