From 6f090e80326b0a9f64666172ff5526d5cae2789a Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 9 Apr 2026 14:48:06 -0500 Subject: [PATCH] Fix flaky metrics test caused by DateTime.UtcNow timer resolution race Replace DateTime.UtcNow timestamp ordering in RecordedMeasurement with a monotonically increasing sequence number using Interlocked.Increment. Under coarse timer resolution (~15.6ms on Windows CI), concurrent calls to RecordObservableInstruments() could produce measurements with identical timestamps, causing Value() to return stale data due to LINQ's stable sort preserving insertion order for equal keys. --- src/Foundatio.TestHarness/Utility/InMemoryMetrics.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Foundatio.TestHarness/Utility/InMemoryMetrics.cs b/src/Foundatio.TestHarness/Utility/InMemoryMetrics.cs index 7af094b11..f148a6b57 100644 --- a/src/Foundatio.TestHarness/Utility/InMemoryMetrics.cs +++ b/src/Foundatio.TestHarness/Utility/InMemoryMetrics.cs @@ -176,7 +176,7 @@ public double Value(string name) where T : struct { var measurement = GetMeasurements(name) .Where(m => m.Name == name) - .OrderByDescending(m => m.Timestamp) + .OrderByDescending(m => m.Sequence) .FirstOrDefault(); return Convert.ToDouble(measurement.Value); @@ -385,6 +385,10 @@ public void Dispose() [DebuggerDisplay("{Name}={Value}")] public struct RecordedMeasurement where T : struct { + private static long _sequenceCounter; + + private static long GetNextSequence() => Interlocked.Increment(ref _sequenceCounter); + public RecordedMeasurement(Instrument instrument, T value, ref ReadOnlySpan> tags, object? state) { @@ -395,7 +399,7 @@ public RecordedMeasurement(Instrument instrument, T value, ref ReadOnlySpan.Empty; State = state; - Timestamp = DateTime.UtcNow; + Sequence = GetNextSequence(); } public Instrument Instrument { get; } @@ -403,5 +407,5 @@ public RecordedMeasurement(Instrument instrument, T value, ref ReadOnlySpan Tags { get; } public object? State { get; } - public DateTime Timestamp { get; } + public long Sequence { get; } }