[Metrics SDK] Implement configurable cardinality limit#4188
Conversation
|
Hi @dbarker Summary of Changes:
This is my first contribution to OpenTelemetry C++. I'd appreciate guidance on:
Looking forward to your feedback! cc: @lalitb |
marcalff
left a comment
There was a problem hiding this comment.
Thanks for the contribution.
There is something wrong the way clang-format was applied, causing spurious changes in many files.
This:
- is most likely incorrect, and will fail CI on clang-format
- makes it difficult to review the real changes from this patch.
When formatting code, a precise version of clang-format must be used. Applying a different version will cause this. The best is to use the dev container to perform formatting, as it will use the exact same version as the github CI.
In any cases, adding a file in a commit that was not changed voluntarily (i.e., with a real fix) is a red flag, and should not happen.
Please rework the patch to remove the noise, so it can be reviewed.
a385928 to
7758926
Compare
|
@marcalff Thank you for the feedback! I've reworked the PR to remove all spurious formatting changes. Current state metric_reader.h
Regarding CI: the only failing check is W3C Distributed Tracing Validation V1 (test_tracestate_key_illegal_vendor_format and test_tracestate_key_length_limit). This failure is unrelated to this PR — the changes here are entirely in the Metrics SDK and do not touch any trace context code. The W3C V1 test suite checks out the latest [w3c/trace-context]HEAD at runtime, and these two tests appear to be failing intermittently across PRs. The PR is now clean and ready for review. Please let me know if you need any clarifications on the implementation approach! |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4188 +/- ##
==========================================
- Coverage 78.13% 78.06% -0.07%
==========================================
Files 440 440
Lines 18727 18771 +44
==========================================
+ Hits 14631 14651 +20
- Misses 4096 4120 +24
🚀 New features to boost your workflow:
|
1f771a3 to
8d2967e
Compare
| auto ctx_ptr = meter_context_.lock(); | ||
| if (ctx_ptr) | ||
| { | ||
| size_t reader_limit = ctx_ptr->GetReaderCardinalityLimit(instrument_descriptor.type_); |
There was a problem hiding this comment.
I think this breaks per-reader semantics. Example: reader A has limit 10, reader B has limit 1000. The storage uses 1000, so reader A can export far more series than configured. This is also a heap/memory regression for low-limit readers.
There was a problem hiding this comment.
One possible fix is to avoid resolving reader-level limits into a single storage-level config. The view-level limit can stay on the shared stream/storage, but the MetricReader fallback should be applied per collector/reader, since each reader may have a different configured limit. So instead of taking the max across readers, the collection path likely needs to use the current CollectorHandle's GetCardinalityLimit(instrument_type) when no view-level limit exists.
There was a problem hiding this comment.
Good catch @lalitb, thanks! You're right, resolving the reader-level fallback into shared storage breaks the per-reader semantics you described.
I've removed that logic here and reverted the storage constructors. For now, this PR only enforces view-level aggregation_cardinality_limit (plus the existing SDK default of 2000). Reader-level limits are still parsed, but not enforced.
I agree the right place to apply the MetricReader fallback is in the collection path on a per-CollectorHandle basis when no view-level limit exists. Since that's a more involved change, I'd rather tackle it in a follow-up PR.
Also fixed the IWYU warning in the latest commit.
Add comprehensive unit tests to cover the cardinality limit implementation: metric_reader_test.cc: - CardinalityLimitOptions: Tests setting and getting all instrument-specific limits - CardinalityLimitOptionsDefaultFallback: Tests fallback to default_limit when specific limits are 0 - CardinalityLimitOptionsMixedConfiguration: Tests mixed configuration scenarios metric_collector_test.cc: - CardinalityLimitDelegation: Tests MetricCollector delegates to MetricReader - MeterContextMaxCardinalityLimit: Tests MeterContext returns max limit across readers - MeterContextCardinalityLimitWithMultipleReaders: Tests complex multi-reader scenarios These tests cover the code paths in: - sdk/src/metrics/metric_reader.cc (GetCardinalityLimit switch statement) - sdk/src/metrics/state/metric_collector.cc (delegation) - sdk/src/metrics/meter_context.cc (max-across-readers logic) - sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h (interface) Addresses Codecov coverage gaps for PR open-telemetry#4188.
2895f04 to
126fba1
Compare
dbarker
left a comment
There was a problem hiding this comment.
Thanks for the contribution. Please see some minor feedback and questions below.
3881f4c to
26d3cb3
Compare
22f8fb7 to
b7f7bcb
Compare
dbarker
left a comment
There was a problem hiding this comment.
Thanks for the updates. Please see feedback below. Approval to follow.
f101219 to
5a9978c
Compare
dbarker
left a comment
There was a problem hiding this comment.
Thanks for the PR! Approved with some feedback below.
4ed7073 to
9fc411c
Compare
@marcalff please approve the PR if all the relevant changes are being addressed according to you. |
Thank you for the approval. |
The open change request might need to be cleared first. @ThomsonTan and @lalitb please also take a look since you have open comments. |
Fixes open-telemetry#3292 Implements configurable aggregation cardinality limits at view-level and reader-level as specified by the OpenTelemetry Metrics SDK spec. Changes: - cardinality_limits.h: New header with CardinalityLimits struct and constexpr kDefaultCardinalityLimit = 2000 - metric_reader.h: Include cardinality_limits.h; add GetCardinalityLimit() and SetCardinalityLimits() methods - metric_reader.cc: Implement per-instrument-type limit lookup (plain setter, no sentinel logic) - metric_collector.h/.cc: GetCardinalityLimit() in CollectorHandle defaults to CardinalityLimits::kDefaultCardinalityLimit; MetricCollector delegates to underlying MetricReader - meter_context.h/.cc: Remove GetReaderCardinalityLimit() — per-reader limits are accessed via GetCollectors() directly - sdk_builder.cc: Add ToCardinalityLimits() helper in anonymous namespace to convert CardinalityLimitsConfiguration (0 = unset) to CardinalityLimits; used by both periodic and pull reader creation paths - cardinality_limits_configuration.h: Initialize default_limit to 2000, per-instrument fields to 0 (unset sentinel); eliminates UB - CHANGELOG.md: Document the new feature - metric_reader_test.cc, metric_collector_test.cc: Unit tests; collector tests use GetCollectors() instead of removed GetReaderCardinalityLimit(); added ReaderCardinalityLimitEnforcedOnCollection integration test - sdk/test/metrics/BUILD: Add //sdk/src/metrics dep to all_tests
9fc411c to
2924ea0
Compare
I've resolved the open change request, @dbarker. |
Fixes #3292
Changes
Implements configurable aggregation cardinality limits at two priority levels
as specified by the OpenTelemetry Metrics SDK specification:
AggregationConfig(enforced today)CardinalityLimitsstruct (parsed and accessible via
GetCardinalityLimit(); per-collector enforcementis a follow-up, see TODO in
SetCardinalityLimits())kDefaultCardinalityLimit = 2000used when neither above is setImplementation Details
cardinality_limits.h(new):CardinalityLimitsstruct with per-instrument-type fields, all defaulting tokDefaultCardinalityLimitconstexpr kDefaultCardinalityLimit = 2000MetricReaderchanges:GetCardinalityLimit(InstrumentType)methodSetCardinalityLimits(const CardinalityLimits &)methodCollectorHandle/MetricCollectorchanges:GetCardinalityLimit()virtual method toCollectorHandle(default returnskDefaultCardinalityLimit)MetricCollectordelegates to its underlyingMetricReaderConfiguration changes:
sdk_builder.cc: Wirescardinality_limitsfrom YAML config viaToCardinalityLimits()helpercardinality_limits_configuration.h: Initialized all fields (default_limit = 2000, others= 0)Checklist
CHANGELOG.mdupdated for non-trivial changes