Skip to content

Add Beholder OTel SDK metric cardinality limit#2225

Merged
jmank88 merged 6 commits into
mainfrom
beholder-otel-limit-metric-cardinality
Jul 16, 2026
Merged

Add Beholder OTel SDK metric cardinality limit#2225
jmank88 merged 6 commits into
mainfrom
beholder-otel-limit-metric-cardinality

Conversation

@pkcll

@pkcll pkcll commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

This PR adds a configurable cardinality limit to the Beholder OTel SDK metric provider. It prevents unbounded memory growth when high-cardinality labels (e.g., event_id, workflow_execution_id) are accidentally attached to metrics.

How sdkmetric.WithCardinalityLimit works

WithCardinalityLimit(n) sets a hard limit on distinct attribute sets (time series) per metric instrument. Each unique combination of attributes creates a separate aggregation state in the SDK.

Example:

requestsTotal.Add(ctx, 1,
    attribute.String("method", method),
    attribute.String("status", status),
)

Creates series:

  • (method=GET, status=200)
  • (method=GET, status=500)
  • (method=POST, status=200)

If someone adds user_id (unbounded), series explode. The limit prevents this.

What happens when the limit is reached?

When the (n+1)th unique attribute set arrives, it does not create a new aggregation. Instead, it is recorded into an overflow bucket with attribute otel.metric.overflow=true:

http_requests_total{method=GET,status=200}     15231
http_requests_total{method=POST,status=500}    183
...
http_requests_total{otel.metric.overflow=true} 428912

The total count remains correct, but per-identity of overflowed combinations is lost.

Important: This does not drop the metric or fail exports. It is a safety net, not a substitute for good metric design.

Scope

  • pkg/beholder/config.go: Add MetricCardinalityLimit field
  • pkg/beholder/meter_provider.go: Wire sdkmetric.WithCardinalityLimit
  • pkg/loop/config.go: Propagate limit via env var CL_TELEMETRY_METRIC_CARDINALITY_LIMIT

Limitations

This limit is per instrument, not global across the application. Unbounded labels should still be avoided or filtered via views; the cardinality limit exists as a last-resort memory guard.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

✅ API Diff Results - github.com/smartcontractkit/chainlink-common

✅ Compatible Changes (4)

pkg/beholder (1)
  • DefaultMetricCardinalityLimit — ➕ Added
pkg/beholder.Config (1)
  • MetricCardinalityLimit — ➕ Added
pkg/beholder.writerClientConfig (1)
  • MetricCardinalityLimit — ➕ Added
pkg/loop.EnvConfig (1)
  • TelemetryMetricCardinalityLimit — ➕ Added

📄 View full apidiff report

@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from 0b3ca6d to ca0b2b4 Compare July 7, 2026 03:16
@jmank88
jmank88 requested a review from pavel-raykov July 7, 2026 13:33
@pkcll
pkcll marked this pull request as ready for review July 7, 2026 16:34
@pkcll
pkcll requested review from a team as code owners July 7, 2026 16:34
Copilot AI review requested due to automatic review settings July 7, 2026 16:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reduces Beholder/OTel metric cardinality by introducing default SDK metric views that filter high-cardinality attributes before export, and by adding an SDK-level per-instrument attribute-set cardinality limit (defaulting to 10,000, configurable via CL_TELEMETRY_METRIC_CARDINALITY_LIMIT, with 0 disabling it). It also wires these metric-provider options consistently across the gRPC, HTTP, and writer/noop Beholder clients.

Changes:

  • Add default metric views that deny/allow specific attribute keys to prevent high-cardinality label export.
  • Add/configure an OTel SDK metric cardinality limit (default 10000, 0 disables) and plumb it through loop env config into Beholder config.
  • Centralize metric provider option construction (mergeMetricViews + appendMeterProviderOptions) and apply it across Beholder client variants.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/loop/server.go Passes the env-configured metric cardinality limit into Beholder config during server startup.
pkg/loop/config.go Adds parsing/defaulting for CL_TELEMETRY_METRIC_CARDINALITY_LIMIT and exports it via AsCmdEnv().
pkg/loop/config_test.go Updates full env config + AsCmdEnv test expectations for the new env var.
pkg/beholder/config.go Extends Beholder config with MetricViewsDisabled and MetricCardinalityLimit; defaults limit to 10000 and disables in test defaults.
pkg/beholder/config_test.go Updates golden config example struct to include the new config fields.
pkg/beholder/testdata/config-example.json Updates golden JSON with MetricViewsDisabled and MetricCardinalityLimit.
pkg/beholder/metricviews/views.go Introduces default attribute-filtering views (global denylist + base-trigger allowlists).
pkg/beholder/metricviews/views_test.go Adds tests validating default views drop intended attributes.
pkg/beholder/meter_provider.go Adds centralized view-merge + meter provider option builder (views + cardinality limit).
pkg/beholder/meter_provider_test.go Adds tests for cardinality limit behavior and view merging.
pkg/beholder/client.go Applies centralized meter provider options to the gRPC exporter path.
pkg/beholder/httpclient.go Applies centralized meter provider options to the HTTP exporter path.
pkg/beholder/noop.go Applies centralized meter provider options to the writer/noop client path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/loop/config.go Outdated
Comment on lines +295 to +297
if e.TelemetryMetricCardinalityLimit != 0 {
add(envTelemetryMetricCardinalityLimit, strconv.Itoa(e.TelemetryMetricCardinalityLimit))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already fixed by an earlier commit (switched the field to *int so unset vs. explicit-0 are distinguishable) — Copilot was reviewing a stale diff.

Comment thread pkg/loop/config.go
Comment on lines +539 to +544
limit, err := strconv.Atoi(v)
if err != nil {
return fmt.Errorf("failed to parse %s: %w", envTelemetryMetricCardinalityLimit, err)
}
e.TelemetryMetricCardinalityLimit = limit
} else {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — parse() now rejects negative values with an explicit error; added a test case.

Comment thread pkg/beholder/meter_provider_test.go Outdated
assert.Equal(t, int64(uniqueAttributes), total)
}

func TestMergeMetricViews_prependsDefaults(t *testing.T) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — renamed to TestMergeMetricViews_appendsDefaultsAfterCallerViews.

Comment thread pkg/beholder/metricviews/views_test.go Outdated
Comment on lines +32 to +47
counter.Add(context.Background(), 1,
metric.WithAttributes(
attribute.String("capability_id", "cap-a"),
attribute.String("trigger_id", "trig-a"),
attribute.String("event_id", "ev-1"),
),
)

var rm metricdata.ResourceMetrics
require.NoError(t, reader.Collect(context.Background(), &rm))

keys := attributeKeysFromSum(t, rm)
assert.Contains(t, keys, attribute.Key("capability_id"))
assert.NotContains(t, keys, attribute.Key("trigger_id"))
assert.NotContains(t, keys, attribute.Key("event_id"))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — added an attribute (attempts) that's absent from both the allow- and deny-lists, so the test only passes if the base-trigger view itself applied, not just the global catch-all.

pkcll added a commit that referenced this pull request Jul 7, 2026
INFOPLAT-16436 (see PR #2225 follow-up): trigger_id is needed on the
default metric stream and should only be stripped where it's genuinely
high-cardinality. Remove it from globalHighCardinalityDeny and add it to
baseTriggerAllow so capabilities_base_trigger_* metrics keep passing it
through, matching the existing stopped-resending allow-list.
pkcll added a commit that referenced this pull request Jul 7, 2026
INFOPLAT-16436 (see PR #2225 follow-up): trigger_id is needed on the
default metric stream and should only be stripped where it's genuinely
high-cardinality. Remove it from globalHighCardinalityDeny and add it to
baseTriggerAllow so capabilities_base_trigger_* metrics keep passing it
through, matching the existing stopped-resending allow-list.
@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from 1203e1b to 8231c54 Compare July 7, 2026 20:42
@pkcll

pkcll commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Response to Copilot review comments:

# Finding Status
1 AsCmdEnv drops 0 → child re-defaults to 10000 Already fixed by an earlier commit (switched the field to *int so unset vs. explicit-0 are distinguishable) — Copilot was reviewing a stale diff
2 Negative limit values silently accepted Fixed — parse() now rejects negative values with an explicit error; added a test case
3 Test name says "prepends", code appends Fixed — renamed to TestMergeMetricViews_appendsDefaultsAfterCallerViews
4 Base-trigger allowlist test doesn't prove the specific view matched Fixed — added an attribute (attempts) that's absent from both the allow- and deny-lists, so the test only passes if the base-trigger view itself applied, not just the global catch-all

Comment thread pkg/beholder/metricviews/views.go Outdated
)

var (
globalHighCardinalityDeny = attribute.NewDenyKeysFilter(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this file reads as if it is applied globally to all metrics, instead these are specific limiters for capabilities - is this intended?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes this is global filter for labels which were added. Historically workflow_execution_id was added multiple times for metrics in multiple places. This view/filter attempts to prevent this from happening in the future for the top offenders. This is basically a black list for labels which we know for sure are not bounded.

Comment thread pkg/beholder/metricviews/views.go Outdated
)

baseTriggerAllow = attribute.NewAllowKeysFilter(
attribute.Key("capability_id"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is there some beholder data showing that filtering based on these attributes is needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I dont have exact number for this label cardinality, ideally I would suggest to drop anything that has word ID in it. We should have only low cardinality labels to not put too much pressure on our Observability backends. But thats a long term goal. For now Im keeping this white list here for labels which do look suspicious.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is there a way to compute the traffic of _id* labels vs others now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not directly from the OTel pipeline, you'd need to either instrument the otel exporter on the node side or tap into it somewhere in the otel collector upstream. That would require custom code. New VictoriaMetrics backend has a native cardinality API and UI that shows number of time series for metrics per label: https://vm-ui-prod.ops.prod.cldev.sh/select/0/vmui/?#/cardinality?date=2026-07-09&topN=10&focusLabel=event_id

I think it supports wildcard too *_id

https://vm-ui-prod.ops.prod.cldev.sh/select/0/vmui/?#/cardinality?date=2026-07-09&topN=10&focusLabel=*_id

Image

@pkcll pkcll Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Some reports generated from querying VM API
https://vmselect-prod.ops.prod.cldev.sh:8481

Distinct label values

  ┌─────────────────────────────┬───────────────┬───────────────────────┐
  │ Scope                       │ capability_id │ trigger_id            │
  ├─────────────────────────────┼───────────────┼───────────────────────┤
  │ Global (all metrics)        │ 160           │ 117                   │
  ├─────────────────────────────┼───────────────┼───────────────────────┤
  │ capabilities_*              │ 59            │ 117                   │
  ├─────────────────────────────┼───────────────┼───────────────────────┤
  │ capabilities_base_trigger_* │ 59            │ 117                   │
  ├─────────────────────────────┼───────────────┼───────────────────────┤
  │ platform_launcher_*         │ 102           │ 0 (label not present) │
  └─────────────────────────────┴───────────────┴───────────────────────┘

  trigger_id only appears on capabilities_base_trigger_* metrics.
  capability_id also appears on platform_launcher_* (launcher metrics), which adds ~43 extra values beyond the capabilities family.

  ────────────────────────────────────────

  Series count (key metrics)

  ┌─────────────────────────────────────────────────┬──────────────┬────────────────────────┬─────────────────────┐
  │ Metric                                          │ Total series │ Distinct capability_id │ Distinct trigger_id │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_ack_total             │ 215,546      │ 52                     │ 116                 │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_retry_total           │ 3,358        │ 36                     │ 51                  │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_active_registrations  │ 543          │ 57                     │ —                   │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_pending_events        │ 457          │ 55                     │ —                   │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_ack_attempts_bucket   │ 2,155,460    │ 52                     │ 116                 │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ capabilities_base_trigger_time_to_ack_ms_bucket │ 2,586,552    │ (timed out)            │ 116                 │
  ├─────────────────────────────────────────────────┼──────────────┼────────────────────────┼─────────────────────┤
  │ platform_launcher_* (all)                       │ 7,299        │ 102                    │ —                   │
  └─────────────────────────────────────────────────┴──────────────┴────────────────────────┴─────────────────────┘

  Histogram buckets (*_bucket) dominate series count because of the le label — each capability_id/trigger_id is multiplied across bucket boundaries and other labels (donID, host_name, zone, platformEnv, etc.).

  ────────────────────────────────────────

  Top cardinality drivers (ack_total)

  capability_id — up to ~10,912 series per value:
  • evm:ChainSelector:11344663589394136015@1.0.0 (56)
  • evm:ChainSelector:1523760397290643893@1.0.0 (5734951)

  trigger_id — up to ~9,960 series per value (hash-style trigger_reg_* IDs)

  ────────────────────────────────────────

  Notes

  • VM has ~164M total series; global count({capability_id!=""}) and count({trigger_id!=""}) timed out at the 30s query limit.
  • Scoped queries and the label-values API completed successfully and are the reliable approach here.
  • trigger_id cardinality (117) is moderate; series explosion comes from label combinations (especially on histogram metrics), not from hundreds of thousands of unique trigger IDs.

@pkcll pkcll Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

event_id label cardinality — VictoriaMetrics Global
Source: https://vmselect-prod.ops.prod.cldev.sh:8481/select/0/prometheus

Distinct values

  ┌─────────────────────────────┬──────────────────────────┐
  │ Scope                       │ Distinct event_id values │
  ├─────────────────────────────┼──────────────────────────┤
  │ Global                      │ 88,529                   │
  ├─────────────────────────────┼──────────────────────────┤
  │ capabilities_*              │ 88,529                   │
  ├─────────────────────────────┼──────────────────────────┤
  │ capabilities_base_trigger_* │ 88,529                   │
  ├─────────────────────────────┼──────────────────────────┤
  │ platform_launcher_*         │ 0 (label not present)    │
  ├─────────────────────────────┼──────────────────────────┤
  │ VM tsdb status (top labels) │ 72,955                   │
  └─────────────────────────────┴──────────────────────────┘

  event_id is orders of magnitude higher than the other labels we checked:

  ┌───────────────┬────────────────────────┐
  │ Label         │ Global distinct values │
  ├───────────────┼────────────────────────┤
  │ event_id      │ 88,529                 │
  ├───────────────┼────────────────────────┤
  │ capability_id │ 160                    │
  ├───────────────┼────────────────────────┤
  │ trigger_id    │ 117                    │
  └───────────────┴────────────────────────┘

  ────────────────────────────────────────

  Where it appears

  • Only on capabilities_* metrics (same family as trigger_id)
  • Co-labeled with capability_id, trigger_id, donID, host_name, zone, platformEnv, etc. (42 labels total)
  • Not on platform_launcher_* or gauges like active_registrations / pending_events

  Values look like composite hashes, e.g.
  00003f2477...:a873ff00...:0

  ────────────────────────────────────────

  Series count by metric

  ┌─────────────────────────────────────────────────┬──────────────────────┬───────────────────┐
  │ Metric                                          │ Series with event_id │ Distinct event_id │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_ack_total             │ 217,447              │ 82,391            │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_retry_total           │ 2,792                │ 2,792 (1:1)       │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_ack_attempts_bucket   │ 2,174,470            │ 82,391            │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_time_to_ack_ms_bucket │ 2,609,520            │ 82,398            │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_active_registrations  │ —                    │ —                 │
  ├─────────────────────────────────────────────────┼──────────────────────┼───────────────────┤
  │ capabilities_base_trigger_pending_events        │ —                    │ —                 │
  └─────────────────────────────────────────────────┴──────────────────────┴───────────────────┘

  On ack_total, ~2.6 series per event_id on average (other labels multiply). Histogram buckets push total series into the millions.

  ────────────────────────────────────────

  Takeaway

  event_id is the dominant high-cardinality label in this family — ~82k+ unique values vs ~117 trigger_id and ~59 capability_id on the same metrics. It's a strong candidate for aggregation, dropping at scrape/export, or scoping to debug-only metrics.

Comment thread pkg/beholder/config.go Outdated
MetricRetryConfig *RetryConfig
MetricViews []metric.View
// MetricViewsDisabled skips DefaultViews attribute filtering (for tests).
MetricViewsDisabled bool

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is a strange approach - can't you default filters only in the place where they are created in prod? For example, do you also need these filters in staging?

@pkcll pkcll Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry, // MetricViewsDisabled skips DefaultViews attribute filtering (for tests). - this is confusing comment, its not just for tests (will fix that).

MetricViewsDisabled is a feature flag to disable the filters in case we need it for emergency situations. It will be added to core.toml config and passed from the core node, the same as other Beholder/ChIP config parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed 80de377

Comment thread pkg/beholder/meter_provider.go Outdated
// keeps only the first in registration order and drops the rest. If the default
// "*" denylist ran first, caller histogram-bucket views would be silently dropped.
//
// A consequence is that filters do not compose: once a caller view wins for a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this I don't understand - what is the difficulty of making composable filters? (which are the most natural)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The difficulty is that OTel does not support chaining/composing filters out of the box. It picks the first match and applies it. So if you are adding a blacklist view to filter event_id and there is already a view passed from the core to Beholder for existing metrics (we have cases like that for configuring histogram buckets), the blacklist will not be applied.

Comment thread pkg/beholder/meter_provider.go Outdated
Comment thread pkg/beholder/meter_provider.go Outdated
Comment thread pkg/beholder/meter_provider.go Outdated
@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from 80de377 to 9eaf479 Compare July 10, 2026 21:45
@pkcll pkcll changed the title Limit Beholder metric cardinality Add Beholder OTel SDK metric cardinality limit Jul 10, 2026
@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from 9eaf479 to a7dbabb Compare July 10, 2026 23:14
pkcll and others added 2 commits July 13, 2026 13:46
Wire CL_TELEMETRY_METRIC_CARDINALITY_LIMIT through loop EnvConfig and
server into beholder clients. Default limit is 100,000; 0 disables it.
Use *int in EnvConfig so unset vs explicit-disable propagate correctly
across LOOP parent/child processes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Addresses jmank88 review: replace variadic metricOptions with a cfg-only
method; call sites use append(cfg.metricOptions(), ...).
@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from 1215879 to a0f879f Compare July 13, 2026 17:47
Comment thread pkg/loop/config_test.go Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread pkg/beholder/config.go Outdated
MetricCompressor: "gzip",
MetricReaderInterval: 1 * time.Second,
MetricCompressor: "gzip",
MetricCardinalityLimit: 100000,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this 100000 is defined twice (also below). Can we avoid doing that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in d4e5f85 -- extracted DefaultMetricCardinalityLimit = 100000 as a package-level constant in pkg/beholder/config.go, and updated both pkg/beholder/config.go (DefaultConfig) and pkg/loop/config.go (env fallback) to reference it instead of duplicating the literal.

@pkcll
pkcll force-pushed the beholder-otel-limit-metric-cardinality branch from d4e5f85 to 1708680 Compare July 16, 2026 00:41
Comment thread pkg/loop/config_test.go Outdated
TelemetryEmitterExportMaxBatchSize: 100,
TelemetryEmitterMaxQueueSize: 1000,
TelemetryLogStreamingEnabled: false,
TelemetryMetricCardinalityLimit: new(100000),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you skip specifying it here to use the default (which is the same)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

@jmank88
jmank88 enabled auto-merge July 16, 2026 14:23
@jmank88
jmank88 added this pull request to the merge queue Jul 16, 2026
Merged via the queue into main with commit 9626707 Jul 16, 2026
32 of 33 checks passed
@jmank88
jmank88 deleted the beholder-otel-limit-metric-cardinality branch July 16, 2026 14:37
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.

6 participants