namespace NATS.Server.Auth; /// /// Tracks service request latency using a sorted list of samples for percentile calculation. /// Go reference: accounts.go serviceLatency / serviceExportLatencyStats. /// public sealed class ServiceLatencyTracker { private readonly Lock _lock = new(); private readonly List _samples = []; private readonly int _maxSamples; private long _totalRequests; public ServiceLatencyTracker(int maxSamples = 10000) { _maxSamples = maxSamples; } /// Records a latency sample in milliseconds. public void RecordLatency(double latencyMs) { lock (_lock) { if (_samples.Count >= _maxSamples) _samples.RemoveAt(0); _samples.Add(latencyMs); _totalRequests++; } } public double GetP50() => GetPercentile(0.50); public double GetP90() => GetPercentile(0.90); public double GetP99() => GetPercentile(0.99); /// Returns the value at the given percentile (0.0–1.0) over recorded samples. public double GetPercentile(double percentile) { lock (_lock) return ComputePercentile(_samples, percentile); } // Must be called under _lock. private static double ComputePercentile(List samples, double percentile) { if (samples.Count == 0) return 0; var sorted = new List(samples); sorted.Sort(); var index = (int)(percentile * (sorted.Count - 1)); return sorted[index]; } // Must be called under _lock. private static double ComputeAverage(List samples) { if (samples.Count == 0) return 0; var sum = 0.0; foreach (var s in samples) sum += s; return sum / samples.Count; } public long TotalRequests { get { lock (_lock) return _totalRequests; } } public double AverageLatencyMs { get { lock (_lock) return ComputeAverage(_samples); } } public double MinLatencyMs { get { lock (_lock) return _samples.Count == 0 ? 0 : _samples.Min(); } } public double MaxLatencyMs { get { lock (_lock) return _samples.Count == 0 ? 0 : _samples.Max(); } } public int SampleCount { get { lock (_lock) return _samples.Count; } } /// Clears all samples and resets the total request counter. public void Reset() { lock (_lock) { _samples.Clear(); _totalRequests = 0; } } /// Returns an immutable snapshot of the current tracker state. public ServiceLatencySnapshot GetSnapshot() { lock (_lock) { return new ServiceLatencySnapshot( TotalRequests: _totalRequests, P50Ms: ComputePercentile(_samples, 0.50), P90Ms: ComputePercentile(_samples, 0.90), P99Ms: ComputePercentile(_samples, 0.99), AverageMs: ComputeAverage(_samples), MinMs: _samples.Count == 0 ? 0 : _samples.Min(), MaxMs: _samples.Count == 0 ? 0 : _samples.Max(), SampleCount: _samples.Count); } } } public sealed record ServiceLatencySnapshot( long TotalRequests, double P50Ms, double P90Ms, double P99Ms, double AverageMs, double MinMs, double MaxMs, int SampleCount);