Skip to content

Commit

Permalink
Fix slowTickIfNecessary with infrequently used EWMA
Browse files Browse the repository at this point in the history
Refs #3929
  • Loading branch information
aweisberg authored and joschi committed Oct 3, 2024
1 parent 17e6f52 commit e6fd113
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 4 deletions.
8 changes: 8 additions & 0 deletions metrics-core/src/main/java/io/dropwizard/metrics5/EWMA.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ public void update(long n) {
uncounted.add(n);
}

/**
* Set the rate to the smallest possible positive value. Used to avoid calling tick a large number of times.
*/
public void reset() {
uncounted.reset();
rate = Double.MIN_NORMAL;
}

/**
* Mark the passage of time and decay the current rate accordingly.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,28 @@
*/
public class ExponentialMovingAverages implements MovingAverages {

/**
* If ticking would reduce even Long.MAX_VALUE in the 15 minute EWMA below this target then don't bother
* ticking in a loop and instead reset all the EWMAs.
*/
private static final double maxTickZeroTarget = 0.0001;
private static final int maxTicks;
private static final long TICK_INTERVAL = TimeUnit.SECONDS.toNanos(5);

static
{
int m3Ticks = 1;
final EWMA m3 = EWMA.fifteenMinuteEWMA();
m3.update(Long.MAX_VALUE);
do
{
m3.tick();
m3Ticks++;
}
while (m3.getRate(TimeUnit.SECONDS) > maxTickZeroTarget);
maxTicks = m3Ticks;
}

private final EWMA m1Rate = EWMA.oneMinuteEWMA();
private final EWMA m5Rate = EWMA.fiveMinuteEWMA();
private final EWMA m15Rate = EWMA.fifteenMinuteEWMA();
Expand Down Expand Up @@ -54,10 +74,19 @@ public void tickIfNecessary() {
if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) {
sum.add(age);
final long requiredTicks = age / TICK_INTERVAL;
for (long i = 0; i < requiredTicks; i++) {
m1Rate.tick();
m5Rate.tick();
m15Rate.tick();
if (requiredTicks >= maxTicks) {
m1Rate.reset();
m5Rate.reset();
m15Rate.reset();
}
else
{
for (long i = 0; i < requiredTicks; i++)
{
m1Rate.tick();
m5Rate.tick();
m15Rate.tick();
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.dropwizard.metrics5;

import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ExponentialMovingAveragesTest {
@Test
public void testMaxTicks() {
final Clock clock = mock(Clock.class);
when(clock.getTick()).thenReturn(0L, Long.MAX_VALUE);
final ExponentialMovingAverages ema = new ExponentialMovingAverages(clock);
ema.update(Long.MAX_VALUE);
ema.tickIfNecessary();
final long secondNanos = TimeUnit.SECONDS.toNanos(1);
assertEquals(ema.getM1Rate(), Double.MIN_NORMAL * secondNanos, 0.0);
assertEquals(ema.getM5Rate(), Double.MIN_NORMAL * secondNanos, 0.0);
assertEquals(ema.getM15Rate(), Double.MIN_NORMAL * secondNanos, 0.0);
}
}

0 comments on commit e6fd113

Please sign in to comment.