Author: jukka
Date: Tue Mar 25 03:25:41 2014
New Revision: 1581203

URL: http://svn.apache.org/r1581203
Log:
OAK-1586: Implement checkpoint support in DocumentNodeStore

The virtual clock idea is useful enough to be moved from the test case to Clock
Also clarified the ACCURATE clock functionality and added toString() methods

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java?rev=1581203&r1=1581202&r2=1581203&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
 Tue Mar 25 03:25:41 2014
@@ -20,6 +20,7 @@ import java.util.Date;
 import java.util.Random;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Mechanism for keeping track of time at millisecond accuracy.
@@ -130,6 +131,22 @@ public abstract class Clock {
     }
 
     /**
+     * Waits until the given point in time is reached. The current thread
+     * is suspended until the {@link #getTimeIncreasing()} method returns
+     * a time that's equal or greater than the given point in time.
+     *
+     * @param timestamp time in milliseconds since epoch
+     * @throws InterruptedException if the wait was interrupted
+     */
+    public void waitUntil(long timestamp) throws InterruptedException {
+        long now = getTimeIncreasing();
+        while (now < timestamp) {
+            Thread.sleep(timestamp - now);
+            now = getTimeIncreasing();
+        }
+    }
+
+    /**
      * Simple clock implementation based on {@link System#currentTimeMillis()},
      * which is known to be rather slow on some platforms.
      */
@@ -144,6 +161,10 @@ public abstract class Clock {
                 public synchronized long getTime() {
                     return System.currentTimeMillis() + random.nextInt(noise);
                 }
+                @Override
+                public String toString() {
+                    return "Clock.SIMPLE (with noise)";
+                }
             };
         } else {
             return new Clock() {
@@ -151,6 +172,10 @@ public abstract class Clock {
                 public long getTime() {
                     return System.currentTimeMillis();
                 }
+                @Override
+                public String toString() {
+                    return "Clock.SIMPLE";
+                }
             };
         }
     }
@@ -168,16 +193,18 @@ public abstract class Clock {
         private long ns = System.nanoTime();
         @Override
         public synchronized long getTime() {
-            long nsIncrease = Math.max(System.nanoTime() - ns, 0); // >= 0
+            long nowns = System.nanoTime();
+            long nsIncrease = Math.max(nowns - ns, 0); // >= 0
+
             long msIncrease = nsIncrease / NS_IN_MS;
             if (ACCURATE_CLOCK_GRANULARITY > 1) {
                 msIncrease -= msIncrease % ACCURATE_CLOCK_GRANULARITY;
             }
 
-            long now = ms + msIncrease;
-            if (now > ms + SYNC_INTERVAL) {
-                ms = SIMPLE.getTime();
-                ns = System.nanoTime();
+            if (msIncrease < SYNC_INTERVAL) {
+                return ms + msIncrease;
+            } else {
+                long nowms = SIMPLE.getTime();
                 // Check whether the system time jumped ahead or back
                 // from what we'd expect based on the nanosecond interval.
                 // If the jump was small, it was probably caused by low
@@ -186,20 +213,26 @@ public abstract class Clock {
                 // This should still keep clock drift in check as long as
                 // the nanosecond timings drift on average less than 0.5ms
                 // per second.
-                long jump = ms - now;
+                long jump = nowms - (ms + msIncrease);
                 if (0 < jump && jump < 1000) {
-                    ms = now;
-                    ns -= NS_IN_MS / 2;
+                    ms += msIncrease;
+                    ns += msIncrease * NS_IN_MS - NS_IN_MS / 2;
                 } else if (0 > jump && jump > -1000) {
                     // Note that the Math.max(..., 0) above will cause the
                     // reported time to stay constant for a while instead
                     // of going backwards because of this.
-                    ms = now;
-                    ns += NS_IN_MS / 2;
+                    ms += msIncrease;
+                    ns += msIncrease * NS_IN_MS + NS_IN_MS / 2;
+                } else {
+                    ms = nowms;
+                    ns = nowns;
                 }
+                return ms;
             }
-
-            return now;
+        }
+        @Override
+        public String toString() {
+            return "Clock.ACCURATE";
         }
     };
 
@@ -226,6 +259,41 @@ public abstract class Clock {
             return time;
         }
 
+        @Override
+        public String toString() {
+            return "Clock.Fast";
+        }
+
     }
 
-}
\ No newline at end of file
+    /**
+     * A virtual clock that has no connection to the actual system time.
+     * Instead the clock maintains an internal counter that's incremented
+     * atomically whenever the current time is requested. This guarantees
+     * that the reported time signal is always strictly increasing.
+     */
+    public static class Virtual extends Clock {
+
+        private final AtomicLong time = new AtomicLong();
+
+        @Override
+        public long getTime() {
+            return time.getAndIncrement();
+        }
+
+        @Override
+        public void waitUntil(long timestamp) {
+            long now = time.get();
+            while (now < timestamp && !time.compareAndSet(now, timestamp)) {
+                now = time.get();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Clock.Virtual";
+        }
+
+    };
+
+}

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java?rev=1581203&r1=1581202&r2=1581203&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
 Tue Mar 25 03:25:41 2014
@@ -16,11 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.jackrabbit.oak.plugins.document;
 
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -33,9 +30,10 @@ import static org.junit.Assert.assertNul
 
 public class CheckpointsTest {
 
-    private final SimulatedClock clock = new SimulatedClock();
+    private final Clock clock = new Clock.Virtual();
 
-    private final DocumentNodeStore store = new 
DocumentMK.Builder().clock(clock).getNodeStore();
+    private final DocumentNodeStore store =
+            new DocumentMK.Builder().clock(clock).getNodeStore();
 
     @Test
     public void testCheckpointPurge() throws Exception {
@@ -44,7 +42,7 @@ public class CheckpointsTest {
         assertEquals(r1, store.getCheckpoints().getOldestRevisionToKeep());
 
         //Trigger expiry by forwarding the clock to future
-        clock.forwardPast(expiryTime);
+        clock.waitUntil(clock.getTime() + expiryTime + 1);
         assertNull(store.getCheckpoints().getOldestRevisionToKeep());
     }
 
@@ -65,29 +63,16 @@ public class CheckpointsTest {
         //r2 has the later expiry
         assertEquals(r2, store.getCheckpoints().getOldestRevisionToKeep());
 
+        long starttime = clock.getTime();
+
         //Trigger expiry by forwarding the clock to future e1
-        clock.forwardPast(et1);
+        clock.waitUntil(starttime + et1 + 1);
         assertEquals(r2, store.getCheckpoints().getOldestRevisionToKeep());
 
         //Trigger expiry by forwarding the clock to future e2
         //This time no valid checkpoint
-        clock.forwardPast(et2);
+        clock.waitUntil(starttime + et2 + 1);
         assertNull(store.getCheckpoints().getOldestRevisionToKeep());
     }
 
-    private static class SimulatedClock extends Clock {
-        private final AtomicLong time = new AtomicLong();
-
-        @Override
-        public long getTime() {
-            return time.incrementAndGet();
-        }
-
-        /**
-         * Fast forwards the clock to some future time past the given time
-         */
-        public void forwardPast(long futureTime) {
-            time.set(futureTime + 100);
-        }
-    }
 }


Reply via email to