@@ -105,7 +105,7 @@ public async Task OnPublishMetrics_WritesFileAndResetsCounts(bool isAlwaysReadyI
105
105
int delay = 100 ;
106
106
await Task . Delay ( delay ) ;
107
107
108
- await publisher . OnPublishMetrics ( ) ;
108
+ await publisher . OnPublishMetrics ( DateTime . UtcNow ) ;
109
109
110
110
FileInfo [ ] files = GetMetricsFilesSafe ( _metricsFilePath ) ;
111
111
@@ -137,7 +137,7 @@ public async Task OnPublishMetrics_WritesFileAndResetsCounts(bool isAlwaysReadyI
137
137
delay = ( int ) executionDurationMS + 100 ;
138
138
await Task . Delay ( delay ) ;
139
139
140
- await publisher . OnPublishMetrics ( ) ;
140
+ await publisher . OnPublishMetrics ( DateTime . UtcNow ) ;
141
141
142
142
files = GetMetricsFilesSafe ( _metricsFilePath ) ;
143
143
Assert . Equal ( 1 , files . Length ) ;
@@ -180,7 +180,7 @@ public async Task OnPublishMetrics_PurgesOldFiles()
180
180
int delay = ( int ) executionDurationMS + 100 ;
181
181
await Task . Delay ( delay ) ;
182
182
183
- await publisher . OnPublishMetrics ( ) ;
183
+ await publisher . OnPublishMetrics ( DateTime . UtcNow ) ;
184
184
185
185
FileInfo [ ] files = GetMetricsFilesSafe ( _metricsFilePath ) ;
186
186
Assert . Equal ( 5 , files . Length ) ;
@@ -437,6 +437,100 @@ public void FunctionsStartStop_MinimumActivityIntervals_Scenario3()
437
437
Assert . Equal ( 4800 , publisher . FunctionExecutionTimeMS ) ;
438
438
}
439
439
440
+ [ Fact ]
441
+ public async Task OnPublishMetrics_OutstandingActivityIsPublished ( )
442
+ {
443
+ CleanupMetricsFiles ( ) ;
444
+
445
+ var publisher = CreatePublisher ( metricsPublishInterval : TimeSpan . FromHours ( 1 ) , inStandbyMode : false ) ;
446
+
447
+ Assert . Equal ( 1000 , _options . MinimumActivityIntervalMS ) ;
448
+
449
+ Assert . Equal ( 0 , publisher . ActiveFunctionCount ) ;
450
+ Assert . Equal ( 0 , publisher . FunctionExecutionCount ) ;
451
+ Assert . Equal ( 0 , publisher . FunctionExecutionTimeMS ) ;
452
+
453
+ DateTime now = DateTime . UtcNow ;
454
+
455
+ // *** Interval 0 ***
456
+ // one function starts but doesn't complete before the interval ends
457
+ now += TimeSpan . FromMilliseconds ( 200 ) ;
458
+ publisher . OnFunctionStarted ( "foo" , "0" , now ) ;
459
+
460
+ now += TimeSpan . FromMilliseconds ( 1800 ) ;
461
+ await publisher . OnPublishMetrics ( now ) ;
462
+
463
+ FileInfo [ ] files = GetMetricsFilesSafe ( _metricsFilePath ) ;
464
+ var metrics = await ReadMetricsAsync ( files [ 0 ] . FullName , deleteFile : true ) ;
465
+
466
+ // we expect to emit metrics for the duration of the active window
467
+ // while the function continues running
468
+ Assert . Equal ( 1 , publisher . ActiveFunctionCount ) ;
469
+ Assert . Equal ( 0 , metrics . ExecutionCount ) ;
470
+ Assert . Equal ( 1800 , metrics . ExecutionTimeMS ) ;
471
+
472
+ // *** Interval 1 ****
473
+ // another function starts and a short time later the first completes
474
+ now += TimeSpan . FromMilliseconds ( 100 ) ;
475
+ publisher . OnFunctionStarted ( "foo" , "1" , now ) ;
476
+ now += TimeSpan . FromMilliseconds ( 100 ) ;
477
+ publisher . OnFunctionCompleted ( "foo" , "0" , now ) ;
478
+
479
+ // a short time later another function starts
480
+ // then completes a short time later
481
+ now += TimeSpan . FromMilliseconds ( 700 ) ;
482
+ publisher . OnFunctionStarted ( "bar" , "2" , now ) ;
483
+ now += TimeSpan . FromMilliseconds ( 800 ) ;
484
+ publisher . OnFunctionCompleted ( "bar" , "2" , now ) ;
485
+
486
+ Assert . Equal ( 1 , publisher . ActiveFunctionCount ) ;
487
+ Assert . Equal ( 2 , publisher . FunctionExecutionCount ) ;
488
+ Assert . Equal ( 0 , publisher . FunctionExecutionTimeMS ) ;
489
+
490
+ // wait another 300ms then simulate the end of the interval
491
+ now += TimeSpan . FromMilliseconds ( 300 ) ;
492
+ await publisher . OnPublishMetrics ( now ) ;
493
+
494
+ files = GetMetricsFilesSafe ( _metricsFilePath ) ;
495
+ metrics = await ReadMetricsAsync ( files [ 0 ] . FullName , deleteFile : true ) ;
496
+
497
+ // we expect metrics reflecting the activity during the entire window
498
+ Assert . Equal ( 1 , publisher . ActiveFunctionCount ) ;
499
+ Assert . Equal ( 2 , metrics . ExecutionCount ) ;
500
+ Assert . Equal ( 2000 , metrics . ExecutionTimeMS ) ;
501
+
502
+ // *** Interval 2 ***
503
+ // now, a little while later the invocation completes
504
+ // this gets metered for 1000ms (the minimum)
505
+ now += TimeSpan . FromMilliseconds ( 400 ) ;
506
+ publisher . OnFunctionCompleted ( "foo" , "1" , now ) ;
507
+
508
+ // after a short time another function starts then completes
509
+ // because we've metered the previous function for 1000ms,
510
+ // this invocation isn't metered (it falls within the previous period)
511
+ now += TimeSpan . FromMilliseconds ( 200 ) ;
512
+ publisher . OnFunctionStarted ( "foo" , "3" , now ) ;
513
+ now += TimeSpan . FromMilliseconds ( 300 ) ;
514
+ publisher . OnFunctionCompleted ( "foo" , "3" , now ) ;
515
+
516
+ // another function starts and continues running past the
517
+ // end of the second interval
518
+ // at the end of thie interval, while the function has only
519
+ // ran for 700ms this gets rounded up to 1000ms
520
+ now += TimeSpan . FromMilliseconds ( 400 ) ;
521
+ publisher . OnFunctionStarted ( "foo" , "4" , now ) ;
522
+ now += TimeSpan . FromMilliseconds ( 700 ) ;
523
+
524
+ await publisher . OnPublishMetrics ( now ) ;
525
+
526
+ files = GetMetricsFilesSafe ( _metricsFilePath ) ;
527
+ metrics = await ReadMetricsAsync ( files [ 0 ] . FullName , deleteFile : true ) ;
528
+
529
+ Assert . Equal ( 1 , publisher . ActiveFunctionCount ) ;
530
+ Assert . Equal ( 2 , metrics . ExecutionCount ) ;
531
+ Assert . Equal ( 2000 , metrics . ExecutionTimeMS ) ;
532
+ }
533
+
440
534
[ Fact ]
441
535
public void OnFunctionCompleted_NoOutstandingInvocations_IgnoresEvent ( )
442
536
{
@@ -473,9 +567,15 @@ public void CleanupMetricsFiles()
473
567
}
474
568
}
475
569
476
- private static async Task < FlexConsumptionMetricsPublisher . Metrics > ReadMetricsAsync ( string metricsFilePath )
570
+ private static async Task < FlexConsumptionMetricsPublisher . Metrics > ReadMetricsAsync ( string metricsFilePath , bool deleteFile = false )
477
571
{
478
572
string content = await File . ReadAllTextAsync ( metricsFilePath ) ;
573
+
574
+ if ( deleteFile )
575
+ {
576
+ File . Delete ( metricsFilePath ) ;
577
+ }
578
+
479
579
return JsonConvert . DeserializeObject < FlexConsumptionMetricsPublisher . Metrics > ( content ) ;
480
580
}
481
581
0 commit comments