You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2015/10/02 15:52:33 UTC

svn commit: r1706408 - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/ jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/

Author: mduerig
Date: Fri Oct  2 13:52:32 2015
New Revision: 1706408

URL: http://svn.apache.org/viewvc?rev=1706408&view=rev
Log:
JCR-3860: Properly expose missing values in TimeSeries implementations
Add encoding for default value so clients get a chance to identify them. Default is 0 for backward compatibility.

Modified:
    jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java

Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java Fri Oct  2 13:52:32 2015
@@ -54,4 +54,11 @@ public interface TimeSeries {
      */
     long[] getValuePerWeek();
 
+    /**
+     * The value used to encode missing values i.e. for a period where no value was recorded.
+     *
+     * @return  default value
+     */
+    long getMissingValue();
+
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java Fri Oct  2 13:52:32 2015
@@ -30,31 +30,58 @@ public class TimeSeriesAverage implement
     /** Value */
     private final TimeSeries counter;
 
+    /** The value used to encode missing values */
+    private final long missingValue;
+
+    /**
+     * Same as {@link #TimeSeriesAverage(TimeSeries, TimeSeries, long)} passing 0 for the 3rd argument.
+     * @param value         {@code TimeSeries} of values
+     * @param counter       {@code TimeSeries} of counts
+     */
     public TimeSeriesAverage(TimeSeries value, TimeSeries counter) {
+        this(value, counter, 0);
+    }
+
+    /**
+     * @param value         {@code TimeSeries} of values
+     * @param counter       {@code TimeSeries} of counts
+     * @param missingValue  The value used to encode missing values
+     */
+    public TimeSeriesAverage(TimeSeries value, TimeSeries counter, long missingValue) {
         this.value = value;
         this.counter = counter;
+        this.missingValue = missingValue;
     }
 
     //----------------------------------------------------------< TimeSeries >
 
+    @Override
+    public long getMissingValue() {
+        return missingValue;
+    }
+
+    @Override
     public long[] getValuePerSecond() {
         long[] values = value.getValuePerSecond();
         long[] counts = counter.getValuePerSecond();
         return divide(values, counts);
     }
 
+    @Override
     public long[] getValuePerMinute() {
         long[] values = value.getValuePerMinute();
         long[] counts = counter.getValuePerMinute();
         return divide(values, counts);
     }
 
+    @Override
     public synchronized long[] getValuePerHour() {
         long[] values = value.getValuePerHour();
         long[] counts = counter.getValuePerHour();
         return divide(values, counts);
     }
 
+    @Override
     public synchronized long[] getValuePerWeek() {
         long[] values = value.getValuePerWeek();
         long[] counts = counter.getValuePerWeek();
@@ -66,20 +93,20 @@ public class TimeSeriesAverage implement
     /**
      * Per-entry division of two arrays.
      *
-     * @param a array
-     * @param b array
+     * @param v array
+     * @param c array
      * @return result of division
      */
-    private long[] divide(long[] a, long[] b) {
-        long[] c = new long[a.length];
-        for (int i = 0; i < a.length; i++) {
-            if (b[i] != 0) {
-                c[i] = a[i] / b[i];
+    private long[] divide(long[] v, long[] c) {
+        long[] avg = new long[v.length];
+        for (int i = 0; i < v.length; i++) {
+            if (c[i] == 0 || v[i] == value.getMissingValue() || c[i] == counter.getMissingValue()) {
+                avg[i] = missingValue;
             } else {
-                c[i] = 0;
+                avg[i] = v[i] / c[i];
             }
         }
-        return c;
+        return avg;
     }
 
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java Fri Oct  2 13:52:32 2015
@@ -19,17 +19,20 @@
 
 package org.apache.jackrabbit.stats;
 
+import static java.util.Arrays.fill;
+
 import org.apache.jackrabbit.api.stats.TimeSeries;
 
 /**
  * Time series of the maximum value recorded in a period
  */
 public class TimeSeriesMax implements TimeSeries {
-    private final MaxValue max = new MaxValue(0);
-    private final long[] perSecond = new long[60];
-    private final long[] perMinute = new long[60];
-    private final long[] perHour = new long[7 * 24];
-    private final long[] perWeek = new long[3 * 52];
+    private final MaxValue max;
+    private final long missingValue;
+    private final long[] perSecond;
+    private final long[] perMinute;
+    private final long[] perHour;
+    private final long[] perWeek;
 
     /** Current second (index in {@link #perSecond}) */
     private int seconds;
@@ -43,6 +46,25 @@ public class TimeSeriesMax implements Ti
     /** Current week (index in {@link #perWeek}) */
     private int weeks;
 
+    public TimeSeriesMax() {
+        this(0);
+    }
+
+    public TimeSeriesMax(long missingValue) {
+        this.missingValue = missingValue;
+        max = new MaxValue(missingValue);
+        perSecond = newArray(60, missingValue);
+        perMinute = newArray(60, missingValue);
+        perHour = newArray(7 * 24, missingValue);
+        perWeek = newArray(3 * 52, missingValue);
+    }
+
+    private static long[] newArray(int size, long value) {
+        long[] array = new long[size];
+        fill(array, value);
+        return array;
+    }
+
     public void recordValue(long value) {
         max.setIfMaximal(value);
     }
@@ -53,7 +75,7 @@ public class TimeSeriesMax implements Ti
      * second.
      */
     public synchronized void recordOneSecond() {
-        perSecond[seconds++] = max.getAndSetValue(0);
+        perSecond[seconds++] = max.getAndSetValue(missingValue);
         if (seconds == perSecond.length) {
             seconds = 0;
             perMinute[minutes++] = max(perSecond);
@@ -72,6 +94,11 @@ public class TimeSeriesMax implements Ti
     }
 
     @Override
+    public long getMissingValue() {
+        return missingValue;
+    }
+
+    @Override
     public synchronized long[] getValuePerSecond() {
         return cyclicCopyFrom(perSecond, seconds);
     }
@@ -94,11 +121,13 @@ public class TimeSeriesMax implements Ti
     /**
      * Returns the maximum of all entries in the given array.
      */
-    private static long max(long[] array) {
-        long max = Long.MIN_VALUE;
+    private long max(long[] array) {
+        long max = missingValue;
         for (long v : array) {
-            if (v > max) {
+            if (max == missingValue) {
                 max = v;
+            } else if (v != missingValue) {
+                max = Math.max(max, v);
             }
         }
         return max;
@@ -120,22 +149,24 @@ public class TimeSeriesMax implements Ti
         return reverse;
     }
 
-    private static class MaxValue {
-        private long value;
+    private class MaxValue {
+        private long max;
 
-        public MaxValue(long value) {
-            this.value = value;
+        public MaxValue(long max) {
+            this.max = max;
         }
 
         public synchronized long getAndSetValue(long value) {
-            long v = this.value;
-            this.value = value;
+            long v = max;
+            max = value;
             return v;
         }
 
         public synchronized void setIfMaximal(long value) {
-            if (value > this.value) {
-                this.value = value;
+            if (max == missingValue) {
+                max = value;
+            } else if (value != missingValue) {
+                max = Math.max(max, value);
             }
         }
     }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java Fri Oct  2 13:52:32 2015
@@ -18,10 +18,12 @@ package org.apache.jackrabbit.stats;
 
 import static java.lang.Math.round;
 
+import static java.util.Arrays.fill;
+
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.apache.jackrabbit.api.stats.TimeSeries;
 import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type;
+import org.apache.jackrabbit.api.stats.TimeSeries;
 
 /**
  * Recorder of a time series. An instance of this class records (and clears)
@@ -32,41 +34,68 @@ import org.apache.jackrabbit.api.stats.R
 public class TimeSeriesRecorder implements TimeSeries {
 
     /** Value */
-    private final AtomicLong counter = new AtomicLong();
+    private final AtomicLong counter;
 
     /** Whether to reset value each second */
     private final boolean resetValueEachSecond;
 
+    /** The value used to encode missing values */
+    private final long missingValue;
+
     /** Measured value per second over the last minute. */
-    private final long[] valuePerSecond = new long[60];
+    private final long[] valuePerSecond;
 
     /** Measured value per minute over the last hour. */
-    private final long[] valuePerMinute = new long[60];
+    private final long[] valuePerMinute;
 
     /** Measured value per hour over the last week. */
-    private final long[] valuePerHour = new long[7 * 24];
+    private final long[] valuePerHour;
 
     /** Measured value per week over the last three years. */
-    private final long[] valuePerWeek = new long[3 * 52];
+    private final long[] valuePerWeek;
 
     /** Current second (index in {@link #valuePerSecond}) */
-    private int seconds = 0;
+    private int seconds;
 
     /** Current minute (index in {@link #valuePerMinute}) */
-    private int minutes = 0;
+    private int minutes;
 
     /** Current hour (index in {@link #valuePerHour}) */
-    private int hours = 0;
+    private int hours;
 
     /** Current week (index in {@link #valuePerWeek}) */
-    private int weeks = 0;
+    private int weeks;
 
     public TimeSeriesRecorder(Type type) {
         this(type.isResetValueEachSecond());
     }
 
+    /**
+     * Same as {@link #TimeSeriesRecorder(boolean, long)} passing long for the 2nd argument
+     * @param resetValueEachSecond    Whether to reset value each second
+     */
     public TimeSeriesRecorder(boolean resetValueEachSecond) {
+        this(resetValueEachSecond, 0);
+    }
+
+    /**
+     * @param resetValueEachSecond    Whether to reset value each second
+     * @param missingValue            The value used to encode missing values
+     */
+    public TimeSeriesRecorder(boolean resetValueEachSecond, long missingValue) {
         this.resetValueEachSecond = resetValueEachSecond;
+        this.missingValue = missingValue;
+        counter = new AtomicLong(missingValue);
+        valuePerSecond = newArray(60, missingValue);
+        valuePerMinute = newArray(60, missingValue);
+        valuePerHour = newArray(7 * 24, missingValue);
+        valuePerWeek = newArray(3 * 52, missingValue);
+    }
+
+    private static long[] newArray(int size, long value) {
+        long[] array = new long[size];
+        fill(array, value);
+        return array;
     }
 
     /**
@@ -86,7 +115,7 @@ public class TimeSeriesRecorder implemen
      */
     public synchronized void recordOneSecond() {
         if (resetValueEachSecond) {
-            valuePerSecond[seconds++] = counter.getAndSet(0);
+            valuePerSecond[seconds++] = counter.getAndSet(missingValue);
         } else {
             valuePerSecond[seconds++] = counter.get();
         }
@@ -109,18 +138,28 @@ public class TimeSeriesRecorder implemen
 
     //----------------------------------------------------------< TimeSeries >
 
+
+    @Override
+    public long getMissingValue() {
+        return missingValue;
+    }
+
+    @Override
     public synchronized long[] getValuePerSecond() {
         return cyclicCopyFrom(valuePerSecond, seconds);
     }
 
+    @Override
     public synchronized long[] getValuePerMinute() {
         return cyclicCopyFrom(valuePerMinute, minutes);
     }
 
+    @Override
     public synchronized long[] getValuePerHour() {
         return cyclicCopyFrom(valuePerHour, hours);
     }
 
+    @Override
     public synchronized long[] getValuePerWeek() {
         return cyclicCopyFrom(valuePerWeek, weeks);
     }
@@ -135,14 +174,20 @@ public class TimeSeriesRecorder implemen
      */
     private long aggregate(long[] array) {
         long sum = 0;
-        for (int i = 0; i < array.length; i++) {
-
-            sum += array[i];
-        }
-        if (resetValueEachSecond) {
+        int count = 0;
+        for (long value : array) {
+            if (value != missingValue) {
+                count++;
+                sum += value;
+            }
+        }
+        if (count == 0) {
+            return missingValue;
+        } else if (resetValueEachSecond) {
             return sum;
+        } else {
+            return round((double) sum / count);
         }
-        return round((double) sum / array.length);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java Fri Oct  2 13:52:32 2015
@@ -4,15 +4,13 @@ import java.util.concurrent.atomic.Atomi
 
 import junit.framework.TestCase;
 
-/**
- * michid document
- */
 public class TimeSeriesAverageTest extends TestCase {
+    private TimeSeriesAverage avg;
 
     public void testAverage() {
         TimeSeriesRecorder values = new TimeSeriesRecorder(true);
         TimeSeriesRecorder counts = new TimeSeriesRecorder(true);
-        TimeSeriesAverage avg = new TimeSeriesAverage(values, counts);
+        avg = new TimeSeriesAverage(values, counts);
         AtomicLong value = values.getCounter();
         AtomicLong count = counts.getCounter();
 
@@ -89,12 +87,100 @@ public class TimeSeriesAverageTest exten
         assertValues(avg.getValuePerWeek(), 21);
     }
 
-    private static void assertValues(long[] values, long... expected) {
+    public void testAverageWithMissing() {
+        for (long m : new long[]{-42, 42}) {
+            TimeSeriesRecorder values = new TimeSeriesRecorder(true);
+            TimeSeriesRecorder counts = new TimeSeriesRecorder(true);
+            avg = new TimeSeriesAverage(values, counts, m);
+            AtomicLong value = values.getCounter();
+            AtomicLong count = counts.getCounter();
+
+            // initial values
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // no changes in first second
+            values.recordOneSecond();
+            counts.recordOneSecond();
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // 2 seconds
+            value.set(42);
+            count.set(2);
+            values.recordOneSecond();
+            counts.recordOneSecond();
+            assertValues(avg.getValuePerSecond(), 21);
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // no changes in 3rd second
+            values.recordOneSecond();
+            counts.recordOneSecond();
+            assertValues(avg.getValuePerSecond(), avg.getMissingValue(), 21);
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // Division by 0 reported as missing
+            value.set(1);
+            count.set(0);
+            values.recordOneSecond();
+            counts.recordOneSecond();
+
+            // one minute later
+            for (int i = 0; i < 60; i++) {
+                values.recordOneSecond();
+                counts.recordOneSecond();
+            }
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute(), 21);
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // another minute later
+            for (int i = 0; i < 60; i++) {
+                values.recordOneSecond();
+                counts.recordOneSecond();
+            }
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute(), avg.getMissingValue(), 21);
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek());
+
+            // one hour
+            for (int i = 0; i < 60 * 60; i++) {
+                values.recordOneSecond();
+                counts.recordOneSecond();
+            }
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour(), 21);
+            assertValues(avg.getValuePerWeek());
+
+            // one week
+            for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
+                values.recordOneSecond();
+                counts.recordOneSecond();
+            }
+            assertValues(avg.getValuePerSecond());
+            assertValues(avg.getValuePerMinute());
+            assertValues(avg.getValuePerHour());
+            assertValues(avg.getValuePerWeek(), 21);
+        }
+    }
+
+    private void assertValues(long[] values, long... expected) {
         for (int i = 0; i < expected.length; i++) {
             assertEquals(expected[i], values[values.length - i - 1]);
         }
         for (int i = expected.length; i < values.length; i++) {
-            assertEquals(0, values[values.length - i - 1]);
+            assertEquals(avg.getMissingValue(), values[values.length - i - 1]);
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java Fri Oct  2 13:52:32 2015
@@ -2,93 +2,169 @@ package org.apache.jackrabbit.stats;
 
 import junit.framework.TestCase;
 
-/**
- * michid document
- */
 public class TimeSeriesMaxTest extends TestCase {
+    private TimeSeriesMax max;
 
     public void testMax() {
-        TimeSeriesMax values = new TimeSeriesMax();
+        max = new TimeSeriesMax();
 
         // initial values
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // no changes in first second
-        values.recordOneSecond();
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        max.recordOneSecond();
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // 2 seconds
-        values.recordValue(42);
-        values.recordOneSecond();
-        assertValues(values.getValuePerSecond(), 42);
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        max.recordValue(42);
+        max.recordOneSecond();
+        assertValues(max.getValuePerSecond(), 42);
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // no changes in 3rd second
-        values.recordOneSecond();
-        assertValues(values.getValuePerSecond(), 0, 42);
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        max.recordOneSecond();
+        assertValues(max.getValuePerSecond(), 0, 42);
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // 4th second
-        values.recordValue(99);
-        values.recordOneSecond();
-        assertValues(values.getValuePerSecond(), 99, 0, 42);
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        max.recordValue(99);
+        max.recordOneSecond();
+        assertValues(max.getValuePerSecond(), 99, 0, 42);
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // one minute later
         for (int i = 0; i < 60; i++) {
-            values.recordOneSecond();
+            max.recordOneSecond();
         }
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute(), 99);
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute(), 99);
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // another minute later
         for (int i = 0; i < 60; i++) {
-            values.recordOneSecond();
+            max.recordOneSecond();
         }
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute(), 0, 99);
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek());
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute(), 0, 99);
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek());
 
         // one hour
         for (int i = 0; i < 60 * 60; i++) {
-            values.recordOneSecond();
+            max.recordOneSecond();
         }
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour(), 99);
-        assertValues(values.getValuePerWeek());
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour(), 99);
+        assertValues(max.getValuePerWeek());
 
         // one week
         for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
-            values.recordOneSecond();
+            max.recordOneSecond();
         }
-        assertValues(values.getValuePerSecond());
-        assertValues(values.getValuePerMinute());
-        assertValues(values.getValuePerHour());
-        assertValues(values.getValuePerWeek(), 99);
+        assertValues(max.getValuePerSecond());
+        assertValues(max.getValuePerMinute());
+        assertValues(max.getValuePerHour());
+        assertValues(max.getValuePerWeek(), 99);
     }
 
-    private static void assertValues(long[] values, long... expected) {
+    public void testMaxWithMissing() {
+        for (long m : new long[]{-42, 42}) {
+            max = new TimeSeriesMax(m);
+
+            // initial values
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // no changes in first second
+            max.recordOneSecond();
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // 2 seconds
+            max.recordValue(42);
+            max.recordOneSecond();
+            assertValues(max.getValuePerSecond(), 42);
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // no changes in 3rd second
+            max.recordOneSecond();
+            assertValues(max.getValuePerSecond(), max.getMissingValue(), 42);
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // 4th second
+            max.recordValue(99);
+            max.recordOneSecond();
+            assertValues(max.getValuePerSecond(), 99, max.getMissingValue(), 42);
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // one minute later
+            for (int i = 0; i < 60; i++) {
+                max.recordOneSecond();
+            }
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute(), 99);
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // another minute later
+            for (int i = 0; i < 60; i++) {
+                max.recordOneSecond();
+            }
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute(), max.getMissingValue(), 99);
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek());
+
+            // one hour
+            for (int i = 0; i < 60 * 60; i++) {
+                max.recordOneSecond();
+            }
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour(), 99);
+            assertValues(max.getValuePerWeek());
+
+            // one week
+            for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
+                max.recordOneSecond();
+            }
+            assertValues(max.getValuePerSecond());
+            assertValues(max.getValuePerMinute());
+            assertValues(max.getValuePerHour());
+            assertValues(max.getValuePerWeek(), 99);
+        }
+    }
+
+    private void assertValues(long[] values, long... expected) {
         for (int i = 0; i < expected.length; i++) {
             assertEquals(expected[i], values[values.length - i - 1]);
         }
         for (int i = expected.length; i < values.length; i++) {
-            assertEquals(0, values[values.length - i - 1]);
+            assertEquals(max.getMissingValue(), values[values.length - i - 1]);
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java?rev=1706408&r1=1706407&r2=1706408&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java Fri Oct  2 13:52:32 2015
@@ -23,9 +23,10 @@ import org.apache.jackrabbit.api.stats.R
 
 public class TimeSeriesRecorderTest extends TestCase {
 
+    private TimeSeriesRecorder recorder;
+
     public void testCounter() {
-        TimeSeriesRecorder recorder = new TimeSeriesRecorder(
-                RepositoryStatistics.Type.SESSION_READ_COUNTER);
+        recorder = new TimeSeriesRecorder(RepositoryStatistics.Type.SESSION_READ_COUNTER);
         AtomicLong counter = recorder.getCounter();
 
         // initial values
@@ -110,12 +111,103 @@ public class TimeSeriesRecorderTest exte
         assertValues(recorder.getValuePerWeek(), 13);
     }
 
+    public void testCounterWithMissing() {
+        for (long m : new long[]{-42, 42}) {
+            recorder = new TimeSeriesRecorder(true, m);
+            AtomicLong counter = recorder.getCounter();
+
+            // initial values
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // no changes in first second
+            recorder.recordOneSecond();
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // one increment in second
+            counter.set(0);
+            counter.incrementAndGet();
+            recorder.recordOneSecond();
+            assertValues(recorder.getValuePerSecond(), 1);
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // two increments in second
+            counter.set(0);
+            counter.incrementAndGet();
+            counter.incrementAndGet();
+            recorder.recordOneSecond();
+            assertValues(recorder.getValuePerSecond(), 2, 1);
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // no changes in a second
+            recorder.recordOneSecond();
+            assertValues(recorder.getValuePerSecond(), recorder.getMissingValue(), 2, 1);
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // ten increments in a second
+            counter.set(0);
+            counter.addAndGet(10);
+            recorder.recordOneSecond();
+            assertValues(recorder.getValuePerSecond(), 10, recorder.getMissingValue(), 2, 1);
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // one minute
+            for (int i = 0; i < 60; i++) {
+                recorder.recordOneSecond();
+            }
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute(), 13);
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // second minute
+            for (int i = 0; i < 60; i++) {
+                recorder.recordOneSecond();
+            }
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute(), recorder.getMissingValue(), 13);
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek());
+
+            // one hour
+            for (int i = 0; i < 60 * 60; i++) {
+                recorder.recordOneSecond();
+            }
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour(), 13);
+            assertValues(recorder.getValuePerWeek());
+
+            // one week
+            for (int i = 0; i < 7 * 24 * 60 * 60; i++) {
+                recorder.recordOneSecond();
+            }
+            assertValues(recorder.getValuePerSecond());
+            assertValues(recorder.getValuePerMinute());
+            assertValues(recorder.getValuePerHour());
+            assertValues(recorder.getValuePerWeek(), 13);
+        }
+    }
+
     private void assertValues(long[] values, long... expected) {
         for (int i = 0; i < expected.length; i++) {
             assertEquals(expected[i], values[values.length - i - 1]);
         }
         for (int i = expected.length; i < values.length; i++) {
-            assertEquals(0, values[values.length - i - 1]);
+            assertEquals(recorder.getMissingValue(), values[values.length - i - 1]);
         }
     }