INT-4250: Fix Statistics Mean Decay with Time
JIRA: https://jira.spring.io/browse/INT-4250 When retrieving the mean from `ExponentialMovingAverageRate` or `ExponentialMovingAverageRatio` via `getStatistics()` the mean did not decay over time. The mean did decay when using `getMean()`. This was caused by the statistics performance refactoring.
This commit is contained in:
committed by
Artem Bilan
parent
b06db72652
commit
fac04aeece
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009-2016 the original author or authors.
|
* Copyright 2009-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -118,7 +118,7 @@ public class ExponentialMovingAverageRate {
|
|||||||
this.count++; //NOSONAR - false positive, we're synchronized
|
this.count++; //NOSONAR - false positive, we're synchronized
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statistics calc() {
|
private Statistics calcStatic() {
|
||||||
List<Long> copy;
|
List<Long> copy;
|
||||||
long count;
|
long count;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@@ -197,6 +197,15 @@ public class ExponentialMovingAverageRate {
|
|||||||
* @return the mean value
|
* @return the mean value
|
||||||
*/
|
*/
|
||||||
public double getMean() {
|
public double getMean() {
|
||||||
|
return recalcMean(calcStatic());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decay the mean using the current time.
|
||||||
|
* @param staticStats the static statistics.
|
||||||
|
* @return the new mean.
|
||||||
|
*/
|
||||||
|
private double recalcMean(Statistics staticStats) {
|
||||||
long count = this.count;
|
long count = this.count;
|
||||||
count = count > this.retention ? this.retention : count;
|
count = count > this.retention ? this.retention : count;
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
@@ -205,7 +214,7 @@ public class ExponentialMovingAverageRate {
|
|||||||
double t0 = lastTime();
|
double t0 = lastTime();
|
||||||
double t = System.nanoTime() / this.factor;
|
double t = System.nanoTime() / this.factor;
|
||||||
double value = t > t0 ? (t - t0) / this.period : 0;
|
double value = t > t0 ? (t - t0) / this.period : 0;
|
||||||
return count / (count / calc().getMean() + value);
|
return count / (count / staticStats.getMean() + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized double lastTime() {
|
private synchronized double lastTime() {
|
||||||
@@ -221,14 +230,14 @@ public class ExponentialMovingAverageRate {
|
|||||||
* @return the approximate standard deviation
|
* @return the approximate standard deviation
|
||||||
*/
|
*/
|
||||||
public double getStandardDeviation() {
|
public double getStandardDeviation() {
|
||||||
return calc().getStandardDeviation();
|
return calcStatic().getStandardDeviation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the maximum value recorded (not weighted)
|
* @return the maximum value recorded (not weighted)
|
||||||
*/
|
*/
|
||||||
public double getMax() {
|
public double getMax() {
|
||||||
double min = calc().getMin();
|
double min = calcStatic().getMin();
|
||||||
return min > 0 ? 1 / min : 0;
|
return min > 0 ? 1 / min : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +245,7 @@ public class ExponentialMovingAverageRate {
|
|||||||
* @return the minimum value recorded (not weighted)
|
* @return the minimum value recorded (not weighted)
|
||||||
*/
|
*/
|
||||||
public double getMin() {
|
public double getMin() {
|
||||||
double max = calc().getMax();
|
double max = calcStatic().getMax();
|
||||||
return max > 0 ? 1 / max : 0;
|
return max > 0 ? 1 / max : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +253,9 @@ public class ExponentialMovingAverageRate {
|
|||||||
* @return summary statistics (count, mean, standard deviation etc.)
|
* @return summary statistics (count, mean, standard deviation etc.)
|
||||||
*/
|
*/
|
||||||
public Statistics getStatistics() {
|
public Statistics getStatistics() {
|
||||||
return calc();
|
Statistics staticStats = calcStatic();
|
||||||
|
staticStats.setMean(recalcMean(staticStats));
|
||||||
|
return staticStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009-2016 the original author or authors.
|
* Copyright 2009-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -137,7 +137,7 @@ public class ExponentialMovingAverageRatio {
|
|||||||
this.count++; //NOSONAR - false positive, we're synchronized
|
this.count++; //NOSONAR - false positive, we're synchronized
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statistics calc() {
|
private Statistics calcStatic() {
|
||||||
List<Long> copyTimes;
|
List<Long> copyTimes;
|
||||||
List<Integer> copyValues;
|
List<Integer> copyValues;
|
||||||
long count;
|
long count;
|
||||||
@@ -219,7 +219,15 @@ public class ExponentialMovingAverageRatio {
|
|||||||
// Optimistic to start: success rate is 100%
|
// Optimistic to start: success rate is 100%
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
Statistics statistics = calc();
|
return decayMean(calcStatic());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decay the mean using the current time.
|
||||||
|
* @param staticStats the static statistics.
|
||||||
|
* @return the new mean.
|
||||||
|
*/
|
||||||
|
private double decayMean(Statistics statistics) {
|
||||||
double t = System.nanoTime() / this.factor;
|
double t = System.nanoTime() / this.factor;
|
||||||
double mean = statistics.getMean();
|
double mean = statistics.getMean();
|
||||||
double alpha = Math.exp((lastTime() / this.factor - t) * this.lapse);
|
double alpha = Math.exp((lastTime() / this.factor - t) * this.lapse);
|
||||||
@@ -239,28 +247,30 @@ public class ExponentialMovingAverageRatio {
|
|||||||
* @return the approximate standard deviation of the success rate measurements
|
* @return the approximate standard deviation of the success rate measurements
|
||||||
*/
|
*/
|
||||||
public double getStandardDeviation() {
|
public double getStandardDeviation() {
|
||||||
return calc().getStandardDeviation();
|
return calcStatic().getStandardDeviation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the maximum value recorded of the exponential weighted average (per measurement) success rate
|
* @return the maximum value recorded of the exponential weighted average (per measurement) success rate
|
||||||
*/
|
*/
|
||||||
public double getMax() {
|
public double getMax() {
|
||||||
return calc().getMax();
|
return calcStatic().getMax();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the minimum value recorded of the exponential weighted average (per measurement) success rate
|
* @return the minimum value recorded of the exponential weighted average (per measurement) success rate
|
||||||
*/
|
*/
|
||||||
public double getMin() {
|
public double getMin() {
|
||||||
return calc().getMin();
|
return calcStatic().getMin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return summary statistics (count, mean, standard deviation etc.)
|
* @return summary statistics (count, mean, standard deviation etc.)
|
||||||
*/
|
*/
|
||||||
public Statistics getStatistics() {
|
public Statistics getStatistics() {
|
||||||
return calc();
|
Statistics staticStats = calcStatic();
|
||||||
|
staticStats.setMean(decayMean(staticStats));
|
||||||
|
return staticStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ package org.springframework.integration.support.management;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Gary Russell
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public class Statistics {
|
public class Statistics {
|
||||||
@@ -28,7 +29,7 @@ public class Statistics {
|
|||||||
|
|
||||||
private final double max;
|
private final double max;
|
||||||
|
|
||||||
private final double mean;
|
private double mean;
|
||||||
|
|
||||||
private final double standardDeviation;
|
private final double standardDeviation;
|
||||||
|
|
||||||
@@ -62,6 +63,10 @@ public class Statistics {
|
|||||||
return this.mean;
|
return this.mean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMean(double mean) {
|
||||||
|
this.mean = mean;
|
||||||
|
}
|
||||||
|
|
||||||
public double getStandardDeviation() {
|
public double getStandardDeviation() {
|
||||||
return this.standardDeviation;
|
return this.standardDeviation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -106,6 +106,7 @@ public class ExponentialMovingAverageRateTests {
|
|||||||
Thread.sleep(20L);
|
Thread.sleep(20L);
|
||||||
history.increment();
|
history.increment();
|
||||||
double before = history.getMean();
|
double before = history.getMean();
|
||||||
|
Statistics statisticsBefore = history.getStatistics();
|
||||||
long elapsed = System.currentTimeMillis() - t0;
|
long elapsed = System.currentTimeMillis() - t0;
|
||||||
if (elapsed < 50L) {
|
if (elapsed < 50L) {
|
||||||
assertTrue(before > 10);
|
assertTrue(before > 10);
|
||||||
@@ -113,6 +114,7 @@ public class ExponentialMovingAverageRateTests {
|
|||||||
elapsed = System.currentTimeMillis() - t0;
|
elapsed = System.currentTimeMillis() - t0;
|
||||||
if (elapsed < 80L) {
|
if (elapsed < 80L) {
|
||||||
assertThat(history.getMean(), lessThan(before));
|
assertThat(history.getMean(), lessThan(before));
|
||||||
|
assertThat(history.getStatistics().getMean(), lessThan(statisticsBefore.getMean()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.warn("Test took too long to verify mean");
|
logger.warn("Test took too long to verify mean");
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.integration.support.management;
|
package org.springframework.integration.support.management;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
@@ -96,6 +97,13 @@ public class ExponentialMovingAverageRatioTests {
|
|||||||
public void testDecayedMean() throws Exception {
|
public void testDecayedMean() throws Exception {
|
||||||
history.failure(System.nanoTime() - 200000000);
|
history.failure(System.nanoTime() - 200000000);
|
||||||
assertEquals(average(0, Math.exp(-0.4)), history.getMean(), 0.01);
|
assertEquals(average(0, Math.exp(-0.4)), history.getMean(), 0.01);
|
||||||
|
history.success();
|
||||||
|
history.failure();
|
||||||
|
double mean = history.getMean();
|
||||||
|
Statistics statistics = history.getStatistics();
|
||||||
|
Thread.sleep(50);
|
||||||
|
assertThat(history.getMean(), greaterThan(mean));
|
||||||
|
assertThat(history.getStatistics().getMean(), greaterThan(statistics.getMean()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user