@@ -15,7 +15,7 @@ namespace OpenTelemetry.Metrics;
1515public abstract partial class MetricReader
1616{
1717 private readonly HashSet < string > metricStreamNames = new ( StringComparer . OrdinalIgnoreCase ) ;
18- private readonly ConcurrentDictionary < MetricStreamIdentity , Metric > instrumentIdentityToMetric = new ( ) ;
18+ private readonly ConcurrentDictionary < MetricStreamIdentity , Metric ? > instrumentIdentityToMetric = new ( ) ;
1919 private readonly Lock instrumentCreationLock = new ( ) ;
2020 private int metricLimit ;
2121 private int cardinalityLimit ;
@@ -201,7 +201,7 @@ internal void ApplyParentProviderSettings(
201201
202202 private bool TryGetExistingMetric ( in MetricStreamIdentity metricStreamIdentity , [ NotNullWhen ( true ) ] out Metric ? existingMetric )
203203 => this . instrumentIdentityToMetric . TryGetValue ( metricStreamIdentity , out existingMetric )
204- && existingMetric . Active ;
204+ && existingMetric != null && existingMetric . Active ;
205205
206206 private void CreateOrUpdateMetricStreamRegistration ( in MetricStreamIdentity metricStreamIdentity )
207207 {
@@ -268,8 +268,12 @@ private void RemoveMetric(ref Metric? metric)
268268
269269 OpenTelemetrySdkEventSource . Log . MetricInstrumentRemoved ( metric ! . Name , metric . MeterName ) ;
270270
271- var result = this . instrumentIdentityToMetric . TryRemove ( metric . InstrumentIdentity , out var _ ) ;
272- Debug . Assert ( result , "result was false" ) ;
271+ // Note: This is using TryUpdate and NOT TryRemove because there is a
272+ // race condition. If a metric is deactivated and then reactivated in
273+ // the same collection cycle
274+ // instrumentIdentityToMetric[metric.InstrumentIdentity] may already
275+ // point to the new activated metric and not the old deactivated one.
276+ this . instrumentIdentityToMetric . TryUpdate ( metric . InstrumentIdentity , null , metric ) ;
273277
274278 // Note: metric is a reference to the array storage so
275279 // this clears the metric out of the array.
0 commit comments