@@ -175,14 +175,7 @@ void shortLivedPublish() {
175
175
.register (registry );
176
176
177
177
// before step rollover
178
- assertThat (counter .count ()).isZero ();
179
- assertThat (timer .count ()).isZero ();
180
- assertThat (timer .totalTime (MILLISECONDS )).isZero ();
181
- assertThat (summary .count ()).isZero ();
182
- assertThat (summary .totalAmount ()).isZero ();
183
- assertThat (functionCounter .count ()).isZero ();
184
- assertThat (functionTimer .count ()).isZero ();
185
- assertThat (functionTimer .totalTime (MILLISECONDS )).isZero ();
178
+ assertBeforeRollover (counter , timer , summary , functionCounter , functionTimer );
186
179
187
180
registry .close ();
188
181
@@ -224,17 +217,10 @@ void finalPushHasPartialStep() {
224
217
.register (registry );
225
218
226
219
// before step rollover
227
- assertThat (counter .count ()).isZero ();
228
- assertThat (timer .count ()).isZero ();
229
- assertThat (timer .totalTime (MILLISECONDS )).isZero ();
230
- assertThat (summary .count ()).isZero ();
231
- assertThat (summary .totalAmount ()).isZero ();
232
- assertThat (functionCounter .count ()).isZero ();
233
- assertThat (functionTimer .count ()).isZero ();
234
- assertThat (functionTimer .totalTime (MILLISECONDS )).isZero ();
220
+ assertBeforeRollover (counter , timer , summary , functionCounter , functionTimer );
235
221
236
- clock . add ( config .step ());
237
- registry .publish ();
222
+ addTimeWithRolloverOnStepStart ( clock , registry , config , config .step ());
223
+ registry .scheduledPublish ();
238
224
239
225
assertThat (registry .publishedCounterCounts ).hasSize (1 );
240
226
assertThat (registry .publishedCounterCounts .pop ()).isOne ();
@@ -254,7 +240,7 @@ void finalPushHasPartialStep() {
254
240
assertThat (registry .publishedFunctionTimerTotals .pop ()).isEqualTo (53 );
255
241
256
242
// set clock to middle of second step
257
- clock . add ( config .step ().dividedBy (2 ));
243
+ addTimeWithRolloverOnStepStart ( clock , registry , config , config .step ().dividedBy (2 ));
258
244
// record some more values in new step interval
259
245
counter .increment (2 );
260
246
timer .record (6 , MILLISECONDS );
@@ -309,14 +295,7 @@ void publishOnCloseCrossesStepBoundary() {
309
295
.register (registry );
310
296
311
297
// before rollover
312
- assertThat (counter .count ()).isZero ();
313
- assertThat (timer .count ()).isZero ();
314
- assertThat (timer .totalTime (MILLISECONDS )).isZero ();
315
- assertThat (summary .count ()).isZero ();
316
- assertThat (summary .totalAmount ()).isZero ();
317
- assertThat (functionCounter .count ()).isZero ();
318
- assertThat (functionTimer .count ()).isZero ();
319
- assertThat (functionTimer .totalTime (MILLISECONDS )).isZero ();
298
+ assertBeforeRollover (counter , timer , summary , functionCounter , functionTimer );
320
299
321
300
// before publishing, simulate a step boundary being crossed after forced rollover
322
301
// on close and before/during publishing
@@ -342,6 +321,57 @@ void publishOnCloseCrossesStepBoundary() {
342
321
assertThat (registry .publishedFunctionTimerTotals .pop ()).isEqualTo (53 );
343
322
}
344
323
324
+ @ Test
325
+ @ Issue ("#3863" )
326
+ void shouldPublishLastCompletedStepWhenClosingBeforeScheduledPublish () {
327
+ Counter counter = Counter .builder ("counter_3863" ).register (registry );
328
+ Timer timer = Timer .builder ("timer_3863" ).register (registry );
329
+ DistributionSummary summary = DistributionSummary .builder ("summary_3863" ).register (registry );
330
+
331
+ AtomicLong functionValue = new AtomicLong (0 );
332
+ FunctionCounter functionCounter = FunctionCounter
333
+ .builder ("counter.function_3863" , functionValue , AtomicLong ::get )
334
+ .register (registry );
335
+ FunctionTimer functionTimer = FunctionTimer
336
+ .builder ("timer.function_3863" , this , obj -> 3 , obj -> 53 , MILLISECONDS )
337
+ .register (registry );
338
+
339
+ counter .increment ();
340
+ timer .record (5 , MILLISECONDS );
341
+ summary .record (5 );
342
+ functionValue .set (1 );
343
+
344
+ // before rollover
345
+ assertBeforeRollover (counter , timer , summary , functionCounter , functionTimer );
346
+
347
+ addTimeWithRolloverOnStepStart (clock , registry , config , Duration .ofSeconds (60 ));
348
+
349
+ // All new recordings now belong to next step.
350
+ counter .increment (2 );
351
+ timer .record (10 , MILLISECONDS );
352
+ summary .record (10 );
353
+ functionValue .incrementAndGet ();
354
+
355
+ // Simulating the application close behaviour before actual publishing happens.
356
+ registry .close ();
357
+
358
+ assertThat (registry .publishedCounterCounts ).hasSize (2 );
359
+ assertThat (registry .sumAllPublishedValues (registry .publishedCounterCounts )).isEqualTo (3 );
360
+ assertThat (registry .publishedTimerCounts ).hasSize (2 );
361
+ assertThat (registry .sumAllPublishedValues (registry .publishedTimerCounts )).isEqualTo (2 );
362
+ assertThat (registry .sumAllPublishedValues (registry .publishedTimerSumMilliseconds )).isEqualTo (15 );
363
+ assertThat (registry .publishedSummaryCounts ).hasSize (2 );
364
+ assertThat (registry .sumAllPublishedValues (registry .publishedSummaryCounts )).isEqualTo (2 );
365
+ assertThat (registry .sumAllPublishedValues (registry .publishedSummaryTotals )).isEqualTo (15 );
366
+
367
+ assertThat (registry .publishedFunctionCounterCounts ).hasSize (2 );
368
+ assertThat (registry .sumAllPublishedValues (registry .publishedFunctionCounterCounts )).isEqualTo (2 );
369
+
370
+ assertThat (registry .publishedFunctionTimerCounts ).hasSize (2 );
371
+ assertThat (registry .sumAllPublishedValues (registry .publishedFunctionTimerCounts )).isEqualTo (3 );
372
+ assertThat (registry .sumAllPublishedValues (registry .publishedFunctionTimerTotals )).isEqualTo (53 );
373
+ }
374
+
345
375
@ Test
346
376
@ Issue ("#2818" )
347
377
void scheduledRollOver () {
@@ -357,28 +387,17 @@ void scheduledRollOver() {
357
387
.register (registry );
358
388
359
389
// before rollover
360
- assertThat (counter .count ()).isZero ();
361
- assertThat (timer .count ()).isZero ();
362
- assertThat (timer .totalTime (MILLISECONDS )).isZero ();
363
- assertThat (summary .count ()).isZero ();
364
- assertThat (summary .totalAmount ()).isZero ();
365
- assertThat (functionCounter .count ()).isZero ();
366
- assertThat (functionTimer .count ()).isZero ();
367
- assertThat (functionTimer .totalTime (MILLISECONDS )).isZero ();
368
-
369
- clock .addSeconds (60 );
370
- // simulate this being scheduled at the start of the step
371
- registry .pollMetersToRollover ();
390
+ assertBeforeRollover (counter , timer , summary , functionCounter , functionTimer );
372
391
373
- clock . addSeconds ( 1 );
392
+ addTimeWithRolloverOnStepStart ( clock , registry , config , Duration . ofSeconds ( 60 ) );
374
393
// these recordings belong to the current step and should not be published
375
394
counter .increment ();
376
395
timer .record (5 , MILLISECONDS );
377
396
summary .record (8 );
378
- clock . addSeconds (10 );
397
+ addTimeWithRolloverOnStepStart ( clock , registry , config , Duration . ofSeconds (10 ) );
379
398
380
399
// recordings that happened in the previous step should be published
381
- registry .publish ();
400
+ registry .scheduledPublish ();
382
401
assertThat (registry .publishedCounterCounts ).hasSize (1 );
383
402
assertThat (registry .publishedCounterCounts .pop ()).isOne ();
384
403
assertThat (registry .publishedTimerCounts ).hasSize (1 );
@@ -415,6 +434,8 @@ private class MyStepMeterRegistry extends StepMeterRegistry {
415
434
416
435
Deque <Double > publishedFunctionTimerTotals = new ArrayDeque <>();
417
436
437
+ private long lastScheduledPublishStartTime = 0L ;
438
+
418
439
@ Nullable
419
440
Runnable prePublishAction ;
420
441
@@ -438,6 +459,16 @@ protected void publish() {
438
459
.collect (Collectors .toList ());
439
460
}
440
461
462
+ private void scheduledPublish () {
463
+ this .lastScheduledPublishStartTime = clock .wallTime ();
464
+ this .publish ();
465
+ }
466
+
467
+ @ Override
468
+ protected long getLastScheduledPublishStartTime () {
469
+ return lastScheduledPublishStartTime ;
470
+ }
471
+
441
472
private Timer publishTimer (Timer timer ) {
442
473
publishedTimerCounts .add (timer .count ());
443
474
publishedTimerSumMilliseconds .add (timer .totalTime (MILLISECONDS ));
@@ -466,11 +497,52 @@ private DistributionSummary publishSummary(DistributionSummary summary) {
466
497
return summary ;
467
498
}
468
499
500
+ <T extends Number > double sumAllPublishedValues (Deque <T > deque ) {
501
+ double sum = 0 ;
502
+ while (!deque .isEmpty ()) {
503
+ sum += deque .pop ().doubleValue ();
504
+ }
505
+ return sum ;
506
+ }
507
+
469
508
@ Override
470
509
protected TimeUnit getBaseTimeUnit () {
471
510
return TimeUnit .SECONDS ;
472
511
}
473
512
474
513
}
475
514
515
+ private static void assertBeforeRollover (final Counter counter , final Timer timer ,
516
+ final DistributionSummary summary , final FunctionCounter functionCounter ,
517
+ final FunctionTimer functionTimer ) {
518
+ assertThat (counter .count ()).isZero ();
519
+ assertThat (timer .count ()).isZero ();
520
+ assertThat (timer .totalTime (MILLISECONDS )).isZero ();
521
+ assertThat (summary .count ()).isZero ();
522
+ assertThat (summary .totalAmount ()).isZero ();
523
+ assertThat (functionCounter .count ()).isZero ();
524
+ assertThat (functionTimer .count ()).isZero ();
525
+ assertThat (functionTimer .totalTime (MILLISECONDS )).isZero ();
526
+ }
527
+
528
+ /**
529
+ * This method simulates the behaviour StepRegistry will exhibit when rollOver is
530
+ * scheduled on a thread. This calls {@link StepMeterRegistry#pollMetersToRollover()}
531
+ * as soon as the step is crossed.
532
+ */
533
+ private void addTimeWithRolloverOnStepStart (MockClock clock , StepMeterRegistry registry , StepRegistryConfig config ,
534
+ Duration timeToAdd ) {
535
+
536
+ long currentTime = clock .wallTime ();
537
+ long boundaryForNextStep = ((currentTime / config .step ().toMillis ()) + 1 ) * config .step ().toMillis ();
538
+ long timeToNextStep = boundaryForNextStep - currentTime ;
539
+ if (timeToAdd .toMillis () >= timeToNextStep ) {
540
+ clock .add (timeToNextStep , MILLISECONDS );
541
+ registry .pollMetersToRollover ();
542
+ clock .add ((timeToAdd .toMillis () - timeToNextStep ), MILLISECONDS );
543
+ return ;
544
+ }
545
+ clock .add (timeToAdd );
546
+ }
547
+
476
548
}
0 commit comments