open-telemetry / open-telemetry/opentelemetry-cpp

Define and enforce a runtime minimum scale for Base2ExponentialHistogramAggregation

Open
#4,325 0 comments 0 reactions 1 assignee View on GitHub

@ThomsonTan is already working on this.

Since Aug 3, 2026.

bug spec-compliance triage/accepted
Dominant language
C++
Stars
1.4k
Forks
632
Avg merge
1d 13h
Merged PRs (30d)
75

Description

Describe your environment

  • Repository revision: PR #4324
  • Reproduced on Windows with MSVC 19.44 using a CMake/Ninja Release build
  • The runtime scale behavior predates PR #4324; that PR only exposed it during review

Steps to reproduce

Configure a base2 exponential histogram at the lowest currently accepted max_scale_ and max_size_, then record values at both ends of the finite double range:

using namespace opentelemetry::sdk::metrics;

Base2ExponentialHistogramAggregationConfig config;
config.max_scale_ = kMaxScaleMin;  // -10
config.max_size_  = kMaxSizeMin;   // 2

Base2ExponentialHistogramAggregation aggregation{&config};
aggregation.Aggregate((std::numeric_limits<double>::denorm_min)());
aggregation.Aggregate((std::numeric_limits<double>::max)());

const auto point =
    opentelemetry::nostd::get<Base2ExponentialHistogramPointData>(aggregation.ToPoint());
EXPECT_GE(point.scale_, kMaxScaleMin);

The current implementation accepts -10 as the configured maximum scale in aggregation_config.h, but automatic scale reduction never applies a lower bound.

What is the expected behavior?

The aggregation should define and enforce a reasonable runtime minimum scale for every automatic reduction path.

If -10 is selected as the C++ SDK's runtime minimum, point.scale_ should never become less than -10. When an input range still cannot fit within max_size_ at that floor, the implementation should apply an explicit, documented policy while preserving all recorded counts.

What is the actual behavior?

The reproduction produces:

point.scale_ == -11

At scale -10, denorm_min() and max() map to bucket indices -2 and 0. Their three-bucket span exceeds max_size_ == 2, so GetScaleReduction() requests one additional reduction. Base2ExponentialHistogramAggregation::Downscale() then subtracts that reduction without consulting any runtime floor:

const uint32_t scale_reduction =
    GetScaleReduction(start_index, end_index, point_data_.max_buckets_);
Downscale(scale_reduction);
// ...
point_data_.scale_ -= static_cast<int32_t>(by);

Additional context

Related:

  • Reviewer discussion on PR #4324
  • Configuration validation issue #4250

Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.