@@ -8,9 +8,15 @@ namespace Sentry;
8
8
9
9
internal class MetricAggregator : IMetricAggregator
10
10
{
11
+ internal const string DisposingMessage = "Disposing MetricAggregator." ;
12
+ internal const string AlreadyDisposedMessage = "Already disposed MetricAggregator." ;
13
+ internal const string CancelledMessage = "Stopping the Metric Aggregator due to a cancellation." ;
14
+ internal const string ShutdownScheduledMessage = "Shutdown scheduled. Stopping by: {0}." ;
15
+ internal const string ShutdownImmediatelyMessage = "Exiting immediately due to 0 shutdown timeout." ;
16
+ internal const string FlushShutdownMessage = "Shutdown token triggered. Exiting metric aggregator." ;
17
+
11
18
private readonly SentryOptions _options ;
12
19
private readonly IMetricHub _metricHub ;
13
- private readonly TimeSpan _flushInterval ;
14
20
15
21
private readonly SemaphoreSlim _codeLocationLock = new ( 1 , 1 ) ;
16
22
private readonly ReaderWriterLockSlim _bucketsLock = new ReaderWriterLockSlim ( ) ;
@@ -26,20 +32,18 @@ internal class MetricAggregator : IMetricAggregator
26
32
private readonly Lazy < Dictionary < long , ConcurrentDictionary < string , Metric > > > _buckets
27
33
= new ( ( ) => new Dictionary < long , ConcurrentDictionary < string , Metric > > ( ) ) ;
28
34
29
- private long _lastClearedStaleLocations = DateTimeOffset . UtcNow . GetDayBucketKey ( ) ;
30
- private readonly ConcurrentDictionary < long , HashSet < MetricResourceIdentifier > > _seenLocations = new ( ) ;
31
- private Dictionary < long , Dictionary < MetricResourceIdentifier , SentryStackFrame > > _pendingLocations = new ( ) ;
35
+ internal long _lastClearedStaleLocations = DateTimeOffset . UtcNow . GetDayBucketKey ( ) ;
36
+ internal readonly ConcurrentDictionary < long , HashSet < MetricResourceIdentifier > > _seenLocations = new ( ) ;
37
+ internal Dictionary < long , Dictionary < MetricResourceIdentifier , SentryStackFrame > > _pendingLocations = new ( ) ;
32
38
33
- private readonly Task _loopTask ;
39
+ internal readonly Task _loopTask ;
34
40
35
41
internal MetricAggregator ( SentryOptions options , IMetricHub metricHub ,
36
- CancellationTokenSource ? shutdownSource = null ,
37
- bool disableLoopTask = false , TimeSpan ? flushInterval = null )
42
+ CancellationTokenSource ? shutdownSource = null , bool disableLoopTask = false )
38
43
{
39
44
_options = options ;
40
45
_metricHub = metricHub ;
41
46
_shutdownSource = shutdownSource ?? new CancellationTokenSource ( ) ;
42
- _flushInterval = flushInterval ?? TimeSpan . FromSeconds ( 5 ) ;
43
47
44
48
if ( disableLoopTask )
45
49
{
@@ -157,7 +161,7 @@ public void Set(string key,
157
161
}
158
162
159
163
/// <inheritdoc cref="IMetricAggregator.Timing"/>
160
- public void Timing ( string key ,
164
+ public virtual void Timing ( string key ,
161
165
double value ,
162
166
MeasurementUnit . Duration unit = MeasurementUnit . Duration . Second ,
163
167
IDictionary < string , string > ? tags = null ,
@@ -321,12 +325,12 @@ private async Task RunLoopAsync()
321
325
// If the cancellation was signaled, run until the end of the queue or shutdownTimeout
322
326
try
323
327
{
324
- await Task . Delay ( _flushInterval , _shutdownSource . Token ) . ConfigureAwait ( false ) ;
328
+ await Task . Delay ( _options . ShutdownTimeout , _shutdownSource . Token ) . ConfigureAwait ( false ) ;
325
329
}
326
330
// Cancellation requested and no timeout allowed, so exit even if there are more items
327
331
catch ( OperationCanceledException ) when ( _options . ShutdownTimeout == TimeSpan . Zero )
328
332
{
329
- _options . LogDebug ( "Exiting immediately due to 0 shutdown timeout." ) ;
333
+ _options . LogDebug ( ShutdownImmediatelyMessage ) ;
330
334
331
335
await shutdownTimeout . CancelAsync ( ) . ConfigureAwait ( false ) ;
332
336
@@ -335,9 +339,7 @@ private async Task RunLoopAsync()
335
339
// Cancellation requested, scheduled shutdown
336
340
catch ( OperationCanceledException )
337
341
{
338
- _options . LogDebug (
339
- "Shutdown scheduled. Stopping by: {0}." ,
340
- _options . ShutdownTimeout ) ;
342
+ _options . LogDebug ( ShutdownScheduledMessage , _options . ShutdownTimeout ) ;
341
343
342
344
shutdownTimeout . CancelAfterSafe ( _options . ShutdownTimeout ) ;
343
345
@@ -407,15 +409,20 @@ public async Task FlushAsync(bool force = true, CancellationToken cancellationTo
407
409
}
408
410
catch ( OperationCanceledException )
409
411
{
410
- _options . LogInfo ( "Shutdown token triggered. Exiting metric aggregator." ) ;
412
+ _options . LogInfo ( FlushShutdownMessage ) ;
411
413
}
412
414
catch ( Exception exception )
413
415
{
414
416
_options . LogError ( exception , "Error processing metrics." ) ;
415
417
}
416
418
finally
417
419
{
418
- _flushLock . Release ( ) ;
420
+ // If the shutdown token was cancelled before we start this method, we can get here
421
+ // without the _flushLock.CurrentCount (i.e. available threads) having been decremented
422
+ if ( _flushLock . CurrentCount < 1 )
423
+ {
424
+ _flushLock . Release ( ) ;
425
+ }
419
426
}
420
427
}
421
428
@@ -483,9 +490,9 @@ private Dictionary<long, Dictionary<MetricResourceIdentifier, SentryStackFrame>>
483
490
/// <summary>
484
491
/// Clear out stale seen locations once a day
485
492
/// </summary>
486
- private void ClearStaleLocations ( )
493
+ internal void ClearStaleLocations ( DateTimeOffset ? testNow = null )
487
494
{
488
- var now = DateTimeOffset . UtcNow ;
495
+ var now = testNow ?? DateTimeOffset . UtcNow ;
489
496
var today = now . GetDayBucketKey ( ) ;
490
497
if ( _lastClearedStaleLocations == today )
491
498
{
@@ -511,11 +518,11 @@ private void ClearStaleLocations()
511
518
/// <inheritdoc cref="IAsyncDisposable.DisposeAsync"/>
512
519
public async ValueTask DisposeAsync ( )
513
520
{
514
- _options . LogDebug ( "Disposing MetricAggregator." ) ;
521
+ _options . LogDebug ( DisposingMessage ) ;
515
522
516
523
if ( _disposed )
517
524
{
518
- _options . LogDebug ( "Already disposed MetricAggregator." ) ;
525
+ _options . LogDebug ( AlreadyDisposedMessage ) ;
519
526
return ;
520
527
}
521
528
@@ -534,7 +541,7 @@ public async ValueTask DisposeAsync()
534
541
}
535
542
catch ( OperationCanceledException )
536
543
{
537
- _options . LogDebug ( "Stopping the Metric Aggregator due to a cancellation." ) ;
544
+ _options . LogDebug ( CancelledMessage ) ;
538
545
}
539
546
catch ( Exception exception )
540
547
{
0 commit comments