Skip to content

fix(metrics): collapse per-IP destination labels to workload identity to cut TSDB cardinality#280

Merged
blue4209211 merged 2 commits into
mainfrom
fix/metric-cardinality-destination-collapse-clean
Jul 6, 2026
Merged

fix(metrics): collapse per-IP destination labels to workload identity to cut TSDB cardinality#280
blue4209211 merged 2 commits into
mainfrom
fix/metric-cardinality-destination-collapse-clean

Conversation

@mayankpande88

Copy link
Copy Markdown
Contributor

Problem

Dev VictoriaMetrics was firing TargetDown constantly. Root cause: node-agent metric cardinality.

The destination / actual_destination labels on container_net_tcp_* and all L7 metrics carried raw IP:port. Pod IPs churn and recycle constantly (especially on spot nodes), so every reconnect created a brand-new time series — the dominant driver of TSDB churn:

  • ~440k new series / scrape interval (vm_new_timeseries_created_total)
  • vmsingle RSS >7GB → node-pressure evicted on spot → TSDB flap
  • vmagent OOM-crashlooped (1545×, 1Gi) → dropped all 184 targets → TargetDown storm
  • per-node /metrics payloads exceeded vmagent's 16MB maxScrapeSize

Verified the nudgebee backend keys exclusively on the *_workload_name/namespace labels, never the raw destination/actual_destination — so collapsing them is transparent to consumers.

Changes

  • Collapse internal destinations to workload identity. destination/actual_destination now emit namespace/name for internal destinations instead of churning IP:port. External FQDNs and *_workload_* labels unchanged. Gated by --collapse-internal-destinations / COLLAPSE_INTERNAL_DESTINATIONS (default true) kill-switch.
  • container_net_latency_seconds destination_ip collapses the same way via DestinationIPLabelValue.
  • Bugfix: resetAndSetActive uses Add (not Set) after Reset, so connections collapsing to the same workload label sum instead of overwriting — prevents container_net_tcp_active_connections undercounting.
  • Cleanup: removed 7 dead metrics.go Net* Descs (live path is TCPMetrics.collect()).
  • Tests for destinationLabelValue / DestinationIPLabelValue.

Rollout / rollback

Behind COLLAPSE_INTERNAL_DESTINATIONS (default on); set false to restore raw-IP labels. Expect a large drop in active series and vm_new_timeseries_created_total.

… to cut TSDB cardinality

The destination/actual_destination labels on container_net_tcp_* and all L7
metrics carried raw IP:port. Pod IPs churn and recycle constantly (especially on
spot nodes), so every reconnect created a brand-new time series — the dominant
driver of VictoriaMetrics cardinality/churn (observed ~440k new series/interval,
vmsingle >7GB RSS, vmagent OOM-crashlooping and dropping all scrape targets ->
TargetDown storms).

Changes:
- B1: destination/actual_destination now collapse to the resolved workload
  identity (namespace/name) for internal destinations, keeping series stable
  across pod-IP recycling. External FQDNs and the *_workload_* labels are
  unchanged. Gated by --collapse-internal-destinations (COLLAPSE_INTERNAL_DESTINATIONS,
  default true) as a kill-switch. Backend queries key on *_workload_* labels, not
  the raw destination, so this is transparent to consumers.
- B3: container_net_latency_seconds destination_ip collapses the same way via
  DestinationIPLabelValue.
- Bugfix: resetAndSetActive now uses Add instead of Set after Reset, so multiple
  connections that collapse to the same workload label sum instead of
  overwriting — prevents container_net_tcp_active_connections undercounting.
- B5: removed 7 dead metrics.go Net* Descs (live path is TCPMetrics.collect).
- Tests for destinationLabelValue / DestinationIPLabelValue covering FQDN,
  internal-resolved, unresolved, and flag-off branches.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a mechanism to collapse internal IP:port destinations into stable workload identities (namespace/name) when the CollapseInternalDestinations flag is enabled, aiming to reduce TSDB series cardinality and churn. It also cleans up unused metric descriptors. The review feedback highlights a concurrency issue in resetAndSetActive where a lock should be acquired to prevent transient metric drops during scrapes, suggests preserving the port for external unresolved IP destinations in destinationLabelValue, and recommends adding corresponding test coverage.

Comment thread containers/tcp_metrics.go
Comment thread common/net.go
Comment thread common/net_test.go
- resetAndSetActive: hold t.mu.Lock() across Reset+rebuild so a concurrent
  collect() (RLock) never observes a partially-rebuilt/empty active gauge
  (transient dips in container_net_tcp_active_connections). Pre-existing race,
  fixed here.
- destinationLabelValue: guard the resolved-workload branch with
  wl.Name != hp.ip.String() so the IP-echoed-back fallback is treated as
  unresolved (avoids misleading "external/<ip>"); keep IP:port for unresolved
  external destinations (low cardinality, port is useful), only drop the port
  for unresolved private/internal IPs. Consistent with DestinationIPLabelValue.
- tests: cover unresolved-external (keep port) and IP-echoed-back cases.
@blue4209211 blue4209211 merged commit 41c7f12 into main Jul 6, 2026
7 checks passed
@blue4209211 blue4209211 deleted the fix/metric-cardinality-destination-collapse-clean branch July 6, 2026 03:54
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