From 47f1109e3eaa28a921f093feaea85948f6e5bb1e Mon Sep 17 00:00:00 2001 From: ArunPiduguDD Date: Thu, 7 May 2026 00:02:20 +0000 Subject: [PATCH] Widen scopes_per_resource and metrics_per_scope to u16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous u8 typing capped the per-emission cardinality at 255×255 = 65,025 unique metric sites per resource. Widening these two fields to u16 raises the ceiling to ~4.3B, which fits within the existing total_contexts: u32 envelope. Validation arithmetic uses saturating_mul to avoid u32 overflow at the upper bound. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- CHANGELOG.md | 1 + lading_payload/src/opentelemetry/metric.rs | 48 +++++++++---------- .../src/opentelemetry/metric/templates.rs | 4 +- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4698f7c7c..33653a7eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Updated to rand 0.10.x +- Widened `scopes_per_resource` and `metrics_per_scope` in the OpenTelemetry metrics `Contexts` config from `u8` to `u16`, raising the per-emission cardinality ceiling from 65,025 to ~4.3B unique metric sites. ## [0.32.0] ## Changed diff --git a/lading_payload/src/opentelemetry/metric.rs b/lading_payload/src/opentelemetry/metric.rs index 609aa5767..a0bd1821c 100644 --- a/lading_payload/src/opentelemetry/metric.rs +++ b/lading_payload/src/opentelemetry/metric.rs @@ -77,11 +77,11 @@ pub struct Contexts { /// The range of attributes for resources. pub attributes_per_resource: ConfRange, /// The range of scopes that will be generated per resource. - pub scopes_per_resource: ConfRange, + pub scopes_per_resource: ConfRange, /// The range of attributes for each scope. pub attributes_per_scope: ConfRange, /// The range of metrics that will be generated per scope. - pub metrics_per_scope: ConfRange, + pub metrics_per_scope: ConfRange, /// The range of attributes for each metric. pub attributes_per_metric: ConfRange, } @@ -238,7 +238,7 @@ impl Config { ConfRange::Constant(m) => u32::from(m), ConfRange::Inclusive { min, .. } => u32::from(min), }; - u32::from(n) * metrics + u32::from(n).saturating_mul(metrics) } ConfRange::Inclusive { min, .. } => { let metrics = match self.contexts.metrics_per_scope { @@ -246,7 +246,7 @@ impl Config { ConfRange::Constant(m) => u32::from(m), ConfRange::Inclusive { min, .. } => u32::from(min), }; - u32::from(min) * metrics + u32::from(min).saturating_mul(metrics) } }; @@ -258,7 +258,7 @@ impl Config { ConfRange::Constant(m) => u32::from(m), ConfRange::Inclusive { max, .. } => u32::from(max), }; - u32::from(n) * metrics + u32::from(n).saturating_mul(metrics) } ConfRange::Inclusive { max, .. } => { let metrics = match self.contexts.metrics_per_scope { @@ -266,7 +266,7 @@ impl Config { ConfRange::Constant(m) => u32::from(m), ConfRange::Inclusive { max, .. } => u32::from(max), }; - u32::from(max) * metrics + u32::from(max).saturating_mul(metrics) } }; @@ -556,9 +556,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, steps in 1..u8::MAX, ) { @@ -599,9 +599,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, steps in 1..u8::MAX, budget in 128..2048_usize, @@ -647,9 +647,9 @@ mod test { total_contexts_min in 1..4_u32, total_contexts_max in 5..100_u32, attributes_per_resource in 0..25_u8, - scopes_per_resource in 1..10_u8, + scopes_per_resource in 1..10_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 1..10_u8, + metrics_per_scope in 1..10_u16, attributes_per_metric in 0..100_u8, budget in 128..2048_usize, ) { @@ -693,9 +693,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, budget in SMALLEST_PROTOBUF..2048_usize, ) { @@ -767,9 +767,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, steps in 1..u8::MAX, budget in SMALLEST_PROTOBUF..2048_usize, @@ -912,9 +912,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 1..20_u8, + scopes_per_resource in 1..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 1..20_u8, + metrics_per_scope in 1..20_u16, attributes_per_metric in 0..10_u8, budget in SMALLEST_PROTOBUF..4098_usize, ) { @@ -958,9 +958,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, steps in 1..u8::MAX, budget in SMALLEST_PROTOBUF..2048_usize, @@ -1041,9 +1041,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, budget in SMALLEST_PROTOBUF..4098_usize, ) { @@ -1081,9 +1081,9 @@ mod test { seed: u64, total_contexts in 1..1_000_u32, attributes_per_resource in 0..20_u8, - scopes_per_resource in 0..20_u8, + scopes_per_resource in 0..20_u16, attributes_per_scope in 0..20_u8, - metrics_per_scope in 0..20_u8, + metrics_per_scope in 0..20_u16, attributes_per_metric in 0..10_u8, budget in SMALLEST_PROTOBUF..512_usize, // see note below about repetition ) { diff --git a/lading_payload/src/opentelemetry/metric/templates.rs b/lading_payload/src/opentelemetry/metric/templates.rs index 997ef9213..5c21b3233 100644 --- a/lading_payload/src/opentelemetry/metric/templates.rs +++ b/lading_payload/src/opentelemetry/metric/templates.rs @@ -285,7 +285,7 @@ pub(crate) enum Kind { #[derive(Clone, Debug)] pub(crate) struct ScopeTemplateGenerator { - metrics_per_scope: ConfRange, + metrics_per_scope: ConfRange, metric_generator: MetricTemplateGenerator, str_pool: Rc, tags: TagGenerator, @@ -404,7 +404,7 @@ impl<'a> crate::SizedGenerator<'a> for ScopeTemplateGenerator { #[derive(Clone, Debug)] pub(crate) struct ResourceTemplateGenerator { - scopes_per_resource: ConfRange, + scopes_per_resource: ConfRange, attributes_per_resource: ConfRange, scope_generator: ScopeTemplateGenerator, tags: TagGenerator,