There is a known race condition between Update() and TakeSnapshot() in MetricPoint that has been documented in code since Nov 2021 (current location). Creating this issue to track it formally.
The race
Update() and TakeSnapshot() operate on two separate fields — the aggregator value and MetricPointStatus — which cannot be atomically updated together. If Update() writes the value, TakeSnapshot() reads it and resets status to NoCollectPending, then Update() sets status to CollectPending — the next cycle sees CollectPending with a zero/reset aggregator and exports a spurious empty data point.
For Counter/Histogram this is benign (zero delta adds nothing). For Gauge, value=0 is semantically meaningful and could be misleading. When this was originally accepted, sync Gauge did not exist in the spec.
The race window is nanoseconds wide and this has been in production for 4+ years without issues. Tracking here for formal acknowledgment.
References
There is a known race condition between
Update()andTakeSnapshot()inMetricPointthat has been documented in code since Nov 2021 (current location). Creating this issue to track it formally.The race
Update()andTakeSnapshot()operate on two separate fields — the aggregator value andMetricPointStatus— which cannot be atomically updated together. IfUpdate()writes the value,TakeSnapshot()reads it and resets status toNoCollectPending, thenUpdate()sets status toCollectPending— the next cycle seesCollectPendingwith a zero/reset aggregator and exports a spurious empty data point.For Counter/Histogram this is benign (zero delta adds nothing). For Gauge,
value=0is semantically meaningful and could be misleading. When this was originally accepted, sync Gauge did not exist in the spec.The race window is nanoseconds wide and this has been in production for 4+ years without issues. Tracking here for formal acknowledgment.
References