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.
(cherry picked from commit fac04ae)
This commit is contained in:
committed by
Artem Bilan
parent
cb76cfac16
commit
c7cdbc9cf5
@@ -1,14 +1,17 @@
|
||||
/*
|
||||
* 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"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support.management;
|
||||
@@ -19,7 +22,6 @@ import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Cumulative statistics for an event rate with higher weight given to recent data.
|
||||
* Clients call {@link #increment()} when a new event occurs, and then use convenience methods (e.g. {@link #getMean()})
|
||||
@@ -63,7 +65,6 @@ public class ExponentialMovingAverageRate {
|
||||
private final double factor;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param period the period to base the rate measurement (in seconds)
|
||||
* @param lapsePeriod the exponential lapse rate for the rate average (in seconds)
|
||||
@@ -95,7 +96,7 @@ public class ExponentialMovingAverageRate {
|
||||
this.max = 0;
|
||||
this.count = 0;
|
||||
this.times.clear();
|
||||
t0 = System.nanoTime() / this.factor;
|
||||
this.t0 = System.nanoTime() / this.factor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,17 +115,17 @@ public class ExponentialMovingAverageRate {
|
||||
this.times.poll();
|
||||
}
|
||||
this.times.add(t);
|
||||
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;
|
||||
long count;
|
||||
synchronized (this) {
|
||||
copy = new ArrayList<Long>(this.times);
|
||||
count = this.count;
|
||||
}
|
||||
ExponentialMovingAverage rates = new ExponentialMovingAverage(window);
|
||||
ExponentialMovingAverage rates = new ExponentialMovingAverage(this.window);
|
||||
double t0 = 0;
|
||||
double sum = 0;
|
||||
double weight = 0;
|
||||
@@ -141,14 +142,14 @@ public class ExponentialMovingAverageRate {
|
||||
continue;
|
||||
}
|
||||
double delta = t - t0;
|
||||
double value = delta > 0 ? delta / period : 0;
|
||||
double value = delta > 0 ? delta / this.period : 0;
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
if (value < min) {
|
||||
min = value;
|
||||
}
|
||||
double alpha = Math.exp(-delta * lapse);
|
||||
double alpha = Math.exp(-delta * this.lapse);
|
||||
t0 = t;
|
||||
sum = alpha * sum + value;
|
||||
weight = alpha * weight + 1;
|
||||
@@ -185,6 +186,9 @@ public class ExponentialMovingAverageRate {
|
||||
* @return the time in milliseconds since the last measurement
|
||||
*/
|
||||
public double getTimeSinceLastMeasurement() {
|
||||
if (this.count == 0) {
|
||||
return 0;
|
||||
}
|
||||
double t0 = lastTime();
|
||||
return (System.nanoTime() / this.factor - t0);
|
||||
}
|
||||
@@ -193,6 +197,15 @@ public class ExponentialMovingAverageRate {
|
||||
* @return the mean value
|
||||
*/
|
||||
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;
|
||||
count = count > this.retention ? this.retention : count;
|
||||
if (count == 0) {
|
||||
@@ -200,8 +213,8 @@ public class ExponentialMovingAverageRate {
|
||||
}
|
||||
double t0 = lastTime();
|
||||
double t = System.nanoTime() / this.factor;
|
||||
double value = t > t0 ? (t - t0) / period : 0;
|
||||
return count / (count / calc().getMean() + value);
|
||||
double value = t > t0 ? (t - t0) / this.period : 0;
|
||||
return count / (count / staticStats.getMean() + value);
|
||||
}
|
||||
|
||||
private synchronized double lastTime() {
|
||||
@@ -209,7 +222,7 @@ public class ExponentialMovingAverageRate {
|
||||
return this.times.peekLast() / this.factor;
|
||||
}
|
||||
else {
|
||||
return this.t0;
|
||||
return this.t0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,14 +230,14 @@ public class ExponentialMovingAverageRate {
|
||||
* @return the approximate standard deviation
|
||||
*/
|
||||
public double getStandardDeviation() {
|
||||
return calc().getStandardDeviation();
|
||||
return calcStatic().getStandardDeviation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum value recorded (not weighted)
|
||||
*/
|
||||
public double getMax() {
|
||||
double min = calc().getMin();
|
||||
double min = calcStatic().getMin();
|
||||
return min > 0 ? 1 / min : 0;
|
||||
}
|
||||
|
||||
@@ -232,7 +245,7 @@ public class ExponentialMovingAverageRate {
|
||||
* @return the minimum value recorded (not weighted)
|
||||
*/
|
||||
public double getMin() {
|
||||
double max = calc().getMax();
|
||||
double max = calcStatic().getMax();
|
||||
return max > 0 ? 1 / max : 0;
|
||||
}
|
||||
|
||||
@@ -240,7 +253,9 @@ public class ExponentialMovingAverageRate {
|
||||
* @return summary statistics (count, mean, standard deviation etc.)
|
||||
*/
|
||||
public Statistics getStatistics() {
|
||||
return calc();
|
||||
Statistics staticStats = calcStatic();
|
||||
staticStats.setMean(recalcMean(staticStats));
|
||||
return staticStats;
|
||||
}
|
||||
|
||||
@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"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -134,7 +134,7 @@ public class ExponentialMovingAverageRatio {
|
||||
this.count++;//NOSONAR - false positive, we're synchronized
|
||||
}
|
||||
|
||||
private Statistics calc() {
|
||||
private Statistics calcStatic() {
|
||||
List<Long> copyTimes;
|
||||
List<Integer> copyValues;
|
||||
long count;
|
||||
@@ -216,7 +216,15 @@ public class ExponentialMovingAverageRatio {
|
||||
// Optimistic to start: success rate is 100%
|
||||
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 mean = statistics.getMean();
|
||||
double alpha = Math.exp((lastTime() / this.factor - t) * this.lapse);
|
||||
@@ -236,28 +244,30 @@ public class ExponentialMovingAverageRatio {
|
||||
* @return the approximate standard deviation of the success rate measurements
|
||||
*/
|
||||
public double getStandardDeviation() {
|
||||
return calc().getStandardDeviation();
|
||||
return calcStatic().getStandardDeviation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum value recorded of the exponential weighted average (per measurement) success rate
|
||||
*/
|
||||
public double getMax() {
|
||||
return calc().getMax();
|
||||
return calcStatic().getMax();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minimum value recorded of the exponential weighted average (per measurement) success rate
|
||||
*/
|
||||
public double getMin() {
|
||||
return calc().getMin();
|
||||
return calcStatic().getMin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return summary statistics (count, mean, standard deviation etc.)
|
||||
*/
|
||||
public Statistics getStatistics() {
|
||||
return calc();
|
||||
Statistics staticStats = calcStatic();
|
||||
staticStats.setMean(decayMean(staticStats));
|
||||
return staticStats;
|
||||
}
|
||||
|
||||
@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");
|
||||
* 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 Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class Statistics {
|
||||
@@ -28,7 +29,7 @@ public class Statistics {
|
||||
|
||||
private final double max;
|
||||
|
||||
private final double mean;
|
||||
private double mean;
|
||||
|
||||
private final double standardDeviation;
|
||||
|
||||
@@ -62,6 +63,10 @@ public class Statistics {
|
||||
return this.mean;
|
||||
}
|
||||
|
||||
public void setMean(double mean) {
|
||||
this.mean = mean;
|
||||
}
|
||||
|
||||
public double getStandardDeviation() {
|
||||
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"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -102,6 +102,7 @@ public class ExponentialMovingAverageRateTests {
|
||||
Thread.sleep(20L);
|
||||
history.increment();
|
||||
double before = history.getMean();
|
||||
Statistics statisticsBefore = history.getStatistics();
|
||||
long elapsed = System.currentTimeMillis() - t0;
|
||||
if (elapsed < 50L) {
|
||||
assertTrue(before > 10);
|
||||
@@ -109,6 +110,7 @@ public class ExponentialMovingAverageRateTests {
|
||||
elapsed = System.currentTimeMillis() - t0;
|
||||
if (elapsed < 80L) {
|
||||
assertThat(history.getMean(), lessThan(before));
|
||||
assertThat(history.getStatistics().getMean(), lessThan(statisticsBefore.getMean()));
|
||||
}
|
||||
else {
|
||||
logger.warn("Test took too long to verify mean");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.integration.support.management;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
@@ -94,6 +95,13 @@ public class ExponentialMovingAverageRatioTests {
|
||||
public void testDecayedMean() throws Exception {
|
||||
history.failure(System.nanoTime() - 200000000);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user