diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRate.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRate.java index 3dfd087872..44afa0e4a1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRate.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRate.java @@ -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 copy; long count; synchronized (this) { copy = new ArrayList(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 diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRatio.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRatio.java index 29b73fae3f..7393e764e0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRatio.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/ExponentialMovingAverageRatio.java @@ -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 copyTimes; List 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 diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/Statistics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/Statistics.java index e7f57cc615..92fe7d1444 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/Statistics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/Statistics.java @@ -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; } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRateTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRateTests.java index 8c7c28ac5b..ac0b9cd092 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRateTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRateTests.java @@ -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"); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRatioTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRatioTests.java index 998aed3095..b2ae808b2a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRatioTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/management/ExponentialMovingAverageRatioTests.java @@ -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