Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype exposing the exemplar reservoir for upcoming spec changes #5960

Conversation

jsuereth
Copy link
Contributor

@jsuereth jsuereth commented Nov 3, 2023

  • Create ExemplarReservoirFactory to help abstract over Double/Long hell
  • Create AggregationExtension that allows specifying an exemplar reservoir factory
  • Add a test which hacks RNG to make sure this all works correctly.

This is for open discussion on how we want to expose customization of ExemplarReservoir to users. One of the two remaining blockers for stabilizing Exemplar specification.

cc @jack-berg

- Create ExemplarReservoirFactory to help abstract over Double/Long hell
- Create AggregationExtension that allows specifying an exemplar reservoir factory
- Add a test which hacks RNG to make sure this all works correctly.
Copy link

codecov bot commented Nov 3, 2023

Codecov Report

Attention: Patch coverage is 81.57895% with 14 lines in your changes missing coverage. Please review.

Project coverage is 91.03%. Comparing base (efa46a5) to head (1f7225f).
Report is 460 commits behind head on main.

Files Patch % Lines
.../sdk/metrics/internal/view/DefaultAggregation.java 66.66% 5 Missing and 1 partial ⚠️
...cs/internal/exemplar/ExemplarReservoirFactory.java 66.66% 4 Missing ⚠️
...nal/view/Base2ExponentialHistogramAggregation.java 88.88% 1 Missing ⚠️
...try/sdk/metrics/internal/view/DropAggregation.java 0.00% 1 Missing ⚠️
...ernal/view/ExplicitBucketHistogramAggregation.java 87.50% 1 Missing ⚠️
...dk/metrics/internal/view/LastValueAggregation.java 91.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5960      +/-   ##
============================================
- Coverage     91.09%   91.03%   -0.06%     
- Complexity     5571     5579       +8     
============================================
  Files           612      613       +1     
  Lines         16425    16467      +42     
  Branches       1637     1637              
============================================
+ Hits          14962    14991      +29     
- Misses         1012     1024      +12     
- Partials        451      452       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

View.builder()
.setAggregation(
((AggregationExtension) Aggregation.sum())
.setExemplarReservoirFactory(reservoirFactory))
Copy link
Member

Choose a reason for hiding this comment

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

Should exemplar reservoir be a configuration parameter that hangs off an aggregation or a top level view config option? I.e. View.builder.setExemplarReservoirFactory(...).setAggregation(..)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the things we need to decide.

  • Right now, ExemplarReservoir (defaults) are decided by aggregation.
  • If a user wanted to leverage "default" aggregation, but different reservoir by-instrument type or aggregator choice, we're actually lacking an interplay between those two. E.g. trying to ensure Explicit buckets are the same between exemplars + histograms.

Personally, I think we could go either way here. I'm happy to extract it out further if we want.

Copy link
Member

Choose a reason for hiding this comment

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

Another interesting side affect of having exemplar reservoir be a configurable property of Aggregation is that it causes the reservoir be something that MetricReader / MetricExporter have influence over, since MetricReader / MetricExporter can dictate the default Aggregation as a function of instrument kind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually really like that interaction. It allows exporters to optimise exemplar choice for the backend they speak to. Do you think we should encode this in the specification?

Copy link
Member

Choose a reason for hiding this comment

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

I think it ought to be in the spec if we're going to allow that type of configuration in the java implementation.

Another thought: I think we probably should introduce an AggregationBuilder instead of allowing Aggregation instances to be mutated after instantiation with setAggregation.

I propose we add new method static AggregationBuilder Aggregation#builder() for accessing an AggregationBuilder, defined as follows:

public interface AggregationBuilder {

  // Configure aggregation
  AggregationBuilder setDrop();
  AggregationBuilder setSum();
  AggregationBuilder setLastValue();
  AggregationBuilder setExplicitBucketHistogram();
  AggregationBuilder setExplicitBucketHistogram(List<Double> bucketBoundaries);
  AggregationBuilder setBase2ExponentialHistogram();
  AggregationBuilder setBase2ExponentialHistogram(int maxBuckets, int maxScale);

  // Configure reservoir
  AggregationBuilder setExemplarReservoirFactory(Supplier<ExemplarReservoir<?>> factory);

  Aggregation build();
  
}

Usage would be something like:

    SdkMeterProvider.builder()
        .registerView(
            InstrumentSelector.builder().build(),
            View.builder()
                .setAggregation(Aggregation.builder()
                    .setSum()
                    .setExemplarReservoirFactory(ExemplarReservoirFactory.noSamples())
                    .build())
                .build());

@jack-berg
Copy link
Member

Just the one comment but conceptually I think this makes sense.

@jsuereth
Copy link
Contributor Author

jsuereth commented Nov 3, 2023

Wanted to add another comment about things we should likely be exposing to users with this change (once specification is written and publicized):

  • ExemplarReservoirFactory would be exposed in some fashion. We can play with its final API to limit it if we'd like, or even expose it to InstrumentationDescriptor if we think that's necessary. I like keeping it simple, as it is today.
  • ExemplarReservoir the interface would have to be exposed so you can implement it.
  • I also believe that due to the complexities in making an efficient reservoir, we should expose:
    • FixedSizeExemplarReservoir as the baseline abstraction users can use to build their own reservoirs. This would remain abstract.
    • ReservoirCellSelector as the key component a user would be overriding in most implementations.
    • ReservoirCell as the key mechanism to pre-allocate memory and avoid memory issues in reservoirs. This would be marked final.


@Override
public String toString() {
return "fixedSize(" + size + ")";
Copy link
Member

Choose a reason for hiding this comment

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

I'd like to see this split out into a class so we can provide a more conventional toString implementation:

Suggested change
return "fixedSize(" + size + ")";
return "FixedSizeReservoirFactory(" + size + ")";

this(
ExemplarReservoirFactory.fixedSize(
Clock.getDefault(),
Runtime.getRuntime().availableProcessors(),
Copy link
Member

Choose a reason for hiding this comment

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

Do we still think Runtime.getRuntime().availableProcessors() is best if the spec provides the new language:

the default size MAY be
the number of possible concurrent threads (e.g. numer of CPUs) to help reduce
contention. Otherwise, a default size of 1 SHOULD be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wrote the specification with this bit of code in mind.... What would you instead like to see?

The rationale here was a measurable difference in contention.

return new ExplicitBucketHistogramAggregation(bucketBoundaries);
return new ExplicitBucketHistogramAggregation(
bucketBoundaries,
ExemplarReservoirFactory.histogramBucket(Clock.getDefault(), bucketBoundaries));
Copy link
Member

Choose a reason for hiding this comment

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

I realized that we need to use fixed size by default if bucketBoundaries.size() == 0.

View.builder()
.setAggregation(
((AggregationExtension) Aggregation.sum())
.setExemplarReservoirFactory(reservoirFactory))
Copy link
Member

Choose a reason for hiding this comment

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

I think it ought to be in the spec if we're going to allow that type of configuration in the java implementation.

Another thought: I think we probably should introduce an AggregationBuilder instead of allowing Aggregation instances to be mutated after instantiation with setAggregation.

I propose we add new method static AggregationBuilder Aggregation#builder() for accessing an AggregationBuilder, defined as follows:

public interface AggregationBuilder {

  // Configure aggregation
  AggregationBuilder setDrop();
  AggregationBuilder setSum();
  AggregationBuilder setLastValue();
  AggregationBuilder setExplicitBucketHistogram();
  AggregationBuilder setExplicitBucketHistogram(List<Double> bucketBoundaries);
  AggregationBuilder setBase2ExponentialHistogram();
  AggregationBuilder setBase2ExponentialHistogram(int maxBuckets, int maxScale);

  // Configure reservoir
  AggregationBuilder setExemplarReservoirFactory(Supplier<ExemplarReservoir<?>> factory);

  Aggregation build();
  
}

Usage would be something like:

    SdkMeterProvider.builder()
        .registerView(
            InstrumentSelector.builder().build(),
            View.builder()
                .setAggregation(Aggregation.builder()
                    .setSum()
                    .setExemplarReservoirFactory(ExemplarReservoirFactory.noSamples())
                    .build())
                .build());

carlosalberto pushed a commit to open-telemetry/opentelemetry-specification that referenced this pull request Jan 31, 2024
Fixes #2922
Fixes #3812 
Related to #3756

## Changes

- Cleans up language around exposing `ExemplarReservoir`. (Remove TODO,
e.g.)
- Remove `ExemplarFilter` interface but keep configuration options. (see
#3812)
- Clarify / simplify Spec Compliance matrix for existing state of the
Exemplar Specification.


Prototype in java:
open-telemetry/opentelemetry-java#5960
@jack-berg
Copy link
Member

Closing because this is stale. Please re-open when ready to pursue this work.

@jack-berg jack-berg closed this Aug 26, 2024
carlosalberto pushed a commit to carlosalberto/opentelemetry-specification that referenced this pull request Oct 31, 2024
…emetry#3820)

Fixes open-telemetry#2922
Fixes open-telemetry#3812 
Related to open-telemetry#3756

## Changes

- Cleans up language around exposing `ExemplarReservoir`. (Remove TODO,
e.g.)
- Remove `ExemplarFilter` interface but keep configuration options. (see
open-telemetry#3812)
- Clarify / simplify Spec Compliance matrix for existing state of the
Exemplar Specification.


Prototype in java:
open-telemetry/opentelemetry-java#5960
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