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

[sdk-metrics] Stablize MetricPoint reclaim feature for delta aggregation #5956

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions docs/metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,14 +402,10 @@ is used, it is possible to choose a smaller cardinality limit by allowing the
SDK to reclaim unused metric points.

> [!NOTE]
> Reclaim unused metric points feature was introduced in OpenTelemetry .NET
[1.7.0-alpha.1](../../src/OpenTelemetry/CHANGELOG.md#170-alpha1). It is
currently an experimental feature which can be turned on by setting the
environment variable
`OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS=true`. Once the
[OpenTelemetry
Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#overflow-attribute)
become stable, this feature will be turned on by default.
> Metric points reclaim is the default and only behavior for Delta Aggregation
Temporality starting with version 1.10.0. In version 1.7.0 - 1.9.0, it was an
experimental feature that could be enabled by setting the environment variable
`OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS=true`.

### Memory Preallocation

Expand Down
11 changes: 11 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ Notes](../../RELEASENOTES.md).

## Unreleased

* Promote MetricPoint reclaim feature for delta aggregation from experimental to
stable.
Previously, it is an experimental feature which can be turned on by setting
the environment variable
`OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS=true`.
Now that the [OpenTelemetry
Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#overflow-attribute)
has become stable. The feature is the default and the only allowed behavior
without the need to set an environment variable.
([#5956](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5956))

## 1.10.0-rc.1

Released 2024-Nov-01
Expand Down
36 changes: 3 additions & 33 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ internal sealed class AggregatorStore
internal readonly HashSet<string>? TagKeysInteresting;
#endif
internal readonly bool OutputDelta;
internal readonly bool OutputDeltaWithUnusedMetricPointReclaimEnabled;
internal readonly int NumberOfMetricPoints;
internal readonly ConcurrentDictionary<Tags, LookupData>? TagsToMetricPointIndexDictionaryDelta;
internal readonly Func<ExemplarReservoir?>? ExemplarReservoirFactory;
Expand Down Expand Up @@ -61,7 +60,6 @@ internal AggregatorStore(
AggregationType aggType,
AggregationTemporality temporality,
int cardinalityLimit,
bool shouldReclaimUnusedMetricPoints,
ExemplarFilterType? exemplarFilter = null,
Func<ExemplarReservoir?>? exemplarReservoirFactory = null)
{
Expand Down Expand Up @@ -110,9 +108,8 @@ internal AggregatorStore(
// Newer attributes should be added starting at the index: 2
this.metricPointIndex = 1;

this.OutputDeltaWithUnusedMetricPointReclaimEnabled = shouldReclaimUnusedMetricPoints && this.OutputDelta;

if (this.OutputDeltaWithUnusedMetricPointReclaimEnabled)
// Always reclaim unused MetricPoints for Delta aggregation temporality
if (this.OutputDelta)
{
this.availableMetricPoints = new Queue<int>(cardinalityLimit);

Expand Down Expand Up @@ -184,15 +181,10 @@ internal void Update(double value, ReadOnlySpan<KeyValuePair<string, object?>> t
internal int Snapshot()
{
this.batchSize = 0;
if (this.OutputDeltaWithUnusedMetricPointReclaimEnabled)
if (this.OutputDelta)
{
this.SnapshotDeltaWithMetricPointReclaim();
}
else if (this.OutputDelta)
{
var indexSnapshot = Math.Min(this.metricPointIndex, this.NumberOfMetricPoints - 1);
this.SnapshotDelta(indexSnapshot);
}
else
{
var indexSnapshot = Math.Min(this.metricPointIndex, this.NumberOfMetricPoints - 1);
Expand All @@ -203,28 +195,6 @@ internal int Snapshot()
return this.batchSize;
}

internal void SnapshotDelta(int indexSnapshot)
{
for (int i = 0; i <= indexSnapshot; i++)
{
ref var metricPoint = ref this.metricPoints[i];
if (metricPoint.MetricPointStatus == MetricPointStatus.NoCollectPending)
{
continue;
}

this.TakeMetricPointSnapshot(ref metricPoint, outputDelta: true);

this.currentMetricPointBatch[this.batchSize] = i;
this.batchSize++;
}

if (this.EndTimeInclusive != default)
{
this.StartTimeExclusive = this.EndTimeInclusive;
}
}

internal void SnapshotDeltaWithMetricPointReclaim()
{
// Index = 0 is reserved for the case where no dimensions are provided.
Expand Down
10 changes: 1 addition & 9 deletions src/OpenTelemetry/Metrics/MeterProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ namespace OpenTelemetry.Metrics;

internal sealed class MeterProviderSdk : MeterProvider
{
internal const string ReclaimUnusedMetricPointsConfigKey = "OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS";
internal const string ExemplarFilterConfigKey = "OTEL_METRICS_EXEMPLAR_FILTER";
internal const string ExemplarFilterHistogramsConfigKey = "OTEL_DOTNET_EXPERIMENTAL_METRICS_EXEMPLAR_FILTER_HISTOGRAMS";

internal readonly IServiceProvider ServiceProvider;
internal readonly IDisposable? OwnedServiceProvider;
internal int ShutdownCount;
internal bool Disposed;
internal bool ReclaimUnusedMetricPoints;
internal ExemplarFilterType? ExemplarFilter;
internal ExemplarFilterType? ExemplarFilterForHistograms;
internal Action? OnCollectObservableInstruments;
Expand Down Expand Up @@ -73,7 +71,7 @@ internal MeterProviderSdk(
this.viewConfigs = state.ViewConfigs;

OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent(
$"MeterProvider configuration: {{MetricLimit={state.MetricLimit}, CardinalityLimit={state.CardinalityLimit}, ReclaimUnusedMetricPoints={this.ReclaimUnusedMetricPoints}, ExemplarFilter={this.ExemplarFilter}, ExemplarFilterForHistograms={this.ExemplarFilterForHistograms}}}.");
$"MeterProvider configuration: {{MetricLimit={state.MetricLimit}, CardinalityLimit={state.CardinalityLimit}, ExemplarFilter={this.ExemplarFilter}, ExemplarFilterForHistograms={this.ExemplarFilterForHistograms}}}.");

foreach (var reader in state.Readers)
{
Expand All @@ -84,7 +82,6 @@ internal MeterProviderSdk(
reader.ApplyParentProviderSettings(
state.MetricLimit,
state.CardinalityLimit,
this.ReclaimUnusedMetricPoints,
this.ExemplarFilter,
this.ExemplarFilterForHistograms);

Expand Down Expand Up @@ -483,11 +480,6 @@ protected override void Dispose(bool disposing)

private void ApplySpecificationConfigurationKeys(IConfiguration configuration)
{
if (configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, ReclaimUnusedMetricPointsConfigKey, out this.ReclaimUnusedMetricPoints))
{
OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent("Reclaim unused metric point feature enabled via configuration.");
}

var hasProgrammaticExemplarFilterValue = this.ExemplarFilter.HasValue;

if (configuration.TryGetStringValue(ExemplarFilterConfigKey, out var configValue))
Expand Down
2 changes: 0 additions & 2 deletions src/OpenTelemetry/Metrics/Metric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ internal Metric(
MetricStreamIdentity instrumentIdentity,
AggregationTemporality temporality,
int cardinalityLimit,
bool shouldReclaimUnusedMetricPoints,
ExemplarFilterType? exemplarFilter = null,
Func<ExemplarReservoir?>? exemplarReservoirFactory = null)
{
Expand Down Expand Up @@ -192,7 +191,6 @@ internal Metric(
aggType,
temporality,
cardinalityLimit,
shouldReclaimUnusedMetricPoints,
exemplarFilter,
exemplarReservoirFactory);
this.Temporality = temporality;
Expand Down
4 changes: 2 additions & 2 deletions src/OpenTelemetry/Metrics/MetricPoint/MetricPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal MetricPoint(
{
Debug.Assert(aggregatorStore != null, "AggregatorStore was null.");
Debug.Assert(histogramExplicitBounds != null, "Histogram explicit Bounds was null.");
Debug.Assert(!aggregatorStore!.OutputDeltaWithUnusedMetricPointReclaimEnabled || lookupData != null, "LookupData was null.");
Debug.Assert(!aggregatorStore!.OutputDelta || lookupData != null, "LookupData was null.");

this.aggType = aggType;
this.Tags = new ReadOnlyTagCollection(tagKeysAndValues);
Expand Down Expand Up @@ -1086,7 +1086,7 @@ private void CompleteUpdate()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CompleteUpdateWithoutMeasurement()
{
if (this.aggregatorStore.OutputDeltaWithUnusedMetricPointReclaimEnabled)
if (this.aggregatorStore.OutputDelta)
{
Interlocked.Decrement(ref this.ReferenceCount);
}
Expand Down
5 changes: 0 additions & 5 deletions src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public abstract partial class MetricReader
private Metric?[]? metrics;
private Metric[]? metricsCurrentBatch;
private int metricIndex = -1;
private bool reclaimUnusedMetricPoints;
private ExemplarFilterType? exemplarFilter;
private ExemplarFilterType? exemplarFilterForHistograms;

Expand Down Expand Up @@ -81,7 +80,6 @@ internal virtual List<Metric> AddMetricWithNoViews(Instrument instrument)
metricStreamIdentity,
this.GetAggregationTemporality(metricStreamIdentity.InstrumentType),
this.cardinalityLimit,
this.reclaimUnusedMetricPoints,
exemplarFilter);
}
catch (NotSupportedException nse)
Expand Down Expand Up @@ -162,7 +160,6 @@ internal virtual List<Metric> AddMetricWithViews(Instrument instrument, List<Met
metricStreamIdentity,
this.GetAggregationTemporality(metricStreamIdentity.InstrumentType),
metricStreamConfig?.CardinalityLimit ?? this.cardinalityLimit,
this.reclaimUnusedMetricPoints,
exemplarFilter,
metricStreamConfig?.ExemplarReservoirFactory);

Expand All @@ -181,15 +178,13 @@ internal virtual List<Metric> AddMetricWithViews(Instrument instrument, List<Met
internal void ApplyParentProviderSettings(
int metricLimit,
int cardinalityLimit,
bool reclaimUnusedMetricPoints,
ExemplarFilterType? exemplarFilter,
ExemplarFilterType? exemplarFilterForHistograms)
{
this.metricLimit = metricLimit;
this.metrics = new Metric[metricLimit];
this.metricsCurrentBatch = new Metric[metricLimit];
this.cardinalityLimit = cardinalityLimit;
this.reclaimUnusedMetricPoints = reclaimUnusedMetricPoints;
this.exemplarFilter = exemplarFilter;
this.exemplarFilterForHistograms = exemplarFilterForHistograms;
}
Expand Down
24 changes: 5 additions & 19 deletions test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ public abstract class AggregatorTestsBase
private static readonly ExplicitBucketHistogramConfiguration HistogramConfiguration = new() { Boundaries = Metric.DefaultHistogramBounds };
private static readonly MetricStreamIdentity MetricStreamIdentity = new(Instrument, HistogramConfiguration);

private readonly bool shouldReclaimUnusedMetricPoints;
private readonly AggregatorStore aggregatorStore;

protected AggregatorTestsBase(bool shouldReclaimUnusedMetricPoints)
protected AggregatorTestsBase()
{
this.shouldReclaimUnusedMetricPoints = shouldReclaimUnusedMetricPoints;

this.aggregatorStore = new(MetricStreamIdentity, AggregationType.HistogramWithBuckets, AggregationTemporality.Cumulative, 1024, this.shouldReclaimUnusedMetricPoints);
this.aggregatorStore = new(MetricStreamIdentity, AggregationType.HistogramWithBuckets, AggregationTemporality.Cumulative, 1024);
}

[Fact]
Expand Down Expand Up @@ -250,8 +247,7 @@ public void HistogramBucketsDefaultUpdatesForSecondsTest(string meterName, strin
metricStreamIdentity,
AggregationType.Histogram,
AggregationTemporality.Cumulative,
cardinalityLimit: 1024,
this.shouldReclaimUnusedMetricPoints);
cardinalityLimit: 1024);

KnownHistogramBuckets actualHistogramBounds = KnownHistogramBuckets.Default;
if (aggregatorStore.HistogramBounds == Metric.DefaultHistogramBoundsShortSeconds)
Expand Down Expand Up @@ -327,7 +323,6 @@ internal void ExponentialHistogramTests(AggregationType aggregationType, Aggrega
aggregationType,
aggregationTemporality,
cardinalityLimit: 1024,
this.shouldReclaimUnusedMetricPoints,
exemplarsEnabled ? ExemplarFilterType.AlwaysOn : null);

var expectedHistogram = new Base2ExponentialBucketHistogram();
Expand Down Expand Up @@ -435,8 +430,7 @@ internal void ExponentialMaxScaleConfigWorks(int? maxScale)
metricStreamIdentity,
AggregationType.Base2ExponentialHistogram,
AggregationTemporality.Cumulative,
cardinalityLimit: 1024,
this.shouldReclaimUnusedMetricPoints);
cardinalityLimit: 1024);

aggregatorStore.Update(10, Array.Empty<KeyValuePair<string, object?>>());

Expand Down Expand Up @@ -520,15 +514,7 @@ public ThreadArguments(MetricPoint histogramPoint, ManualResetEvent mreToEnsureA
public class AggregatorTests : AggregatorTestsBase
{
public AggregatorTests()
: base(shouldReclaimUnusedMetricPoints: false)
{
}
}

public class AggregatorTestsWithReclaimAttribute : AggregatorTestsBase
{
public AggregatorTestsWithReclaimAttribute()
: base(shouldReclaimUnusedMetricPoints: true)
: base()
{
}
}
22 changes: 4 additions & 18 deletions test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public abstract class MetricApiTestsBase : MetricTestsBase
private static readonly int NumberOfMetricUpdateByEachThread = 100000;
private readonly ITestOutputHelper output;

protected MetricApiTestsBase(ITestOutputHelper output, bool shouldReclaimUnusedMetricPoints)
: base(BuildConfiguration(shouldReclaimUnusedMetricPoints))
protected MetricApiTestsBase(ITestOutputHelper output)
: base(BuildConfiguration())
{
this.output = output;
}
Expand Down Expand Up @@ -1703,15 +1703,9 @@ public void GaugeHandlesNoNewMeasurementsCorrectlyWithTemporality(MetricReaderTe
}
}

internal static IConfiguration BuildConfiguration(bool shouldReclaimUnusedMetricPoints)
internal static IConfiguration BuildConfiguration()
{
var configurationData = new Dictionary<string, string?>();

if (shouldReclaimUnusedMetricPoints)
{
configurationData[ReclaimUnusedMetricPointsConfigKey] = "true";
}

return new ConfigurationBuilder()
.AddInMemoryCollection(configurationData)
.Build();
Expand Down Expand Up @@ -1888,15 +1882,7 @@ public UpdateThreadArguments(ManualResetEvent mreToBlockUpdateThread, ManualRese
public class MetricApiTest : MetricApiTestsBase
{
public MetricApiTest(ITestOutputHelper output)
: base(output, shouldReclaimUnusedMetricPoints: false)
{
}
}

public class MetricApiTestWithReclaimAttribute : MetricApiTestsBase
{
public MetricApiTestWithReclaimAttribute(ITestOutputHelper output)
: base(output, shouldReclaimUnusedMetricPoints: true)
: base(output)
{
}
}
Loading
Loading