using System.Collections.Concurrent; namespace NATS.Server; /// /// Tracks slow consumer events per with optional threshold-based callbacks. /// Go reference: server/client.go — handleSlowConsumer, markConnAsSlow. /// public sealed class SlowConsumerTracker { private readonly ConcurrentDictionary _countsByKind = new(); private long _totalCount; private readonly int _threshold; private Action? _onThresholdExceeded; /// /// When the total count reaches this value the registered callback is fired. /// 0 means no threshold (callback never fires automatically). /// public SlowConsumerTracker(int threshold = 0) { _threshold = threshold; } /// Total slow-consumer events recorded across all s. public long TotalCount => Interlocked.Read(ref _totalCount); /// /// Records one slow-consumer event for the given . /// Increments both the per-kind counter and the total counter. /// Fires the threshold callback if reaches the configured threshold. /// public void RecordSlowConsumer(ClientKind kind) { _countsByKind.AddOrUpdate(kind, 1L, static (_, existing) => existing + 1L); var total = Interlocked.Increment(ref _totalCount); if (_threshold > 0 && total >= _threshold) _onThresholdExceeded?.Invoke(kind); } /// Returns the number of slow-consumer events recorded for . public long GetCount(ClientKind kind) => _countsByKind.TryGetValue(kind, out var count) ? count : 0L; /// /// Registers a callback that is invoked (with the triggering ) /// each time the total count reaches or exceeds the configured threshold. /// public void OnThresholdExceeded(Action callback) => _onThresholdExceeded = callback; /// Resets all per-kind and total counters to zero. public void Reset() { _countsByKind.Clear(); Interlocked.Exchange(ref _totalCount, 0L); } }