Author: chetanm
Date: Mon Mar 31 07:44:43 2014
New Revision: 1583251

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

Adding support for collecting expired checkpoints in create path. This should 
help in mitigating the case where checkpoints are frequently created (OAK-1647) 
.

 This should fare better compared to current impl where checkpoint were only 
deleted when oldest checkpoint is fetched

Also added an ignored testcase for OAK-1648

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.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/plugins/document/Checkpoints.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java?rev=1583251&r1=1583250&r2=1583251&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java
 Mon Mar 31 07:44:43 2014
@@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.Map;
 import java.util.SortedMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.annotation.CheckForNull;
 
@@ -40,12 +41,22 @@ class Checkpoints {
      */
     private static final String PROP_CHECKPOINT = "data";
 
+    /**
+     * Number of create calls after which old expired checkpoints entries would
+     * be removed
+     */
+    static final int CLEANUP_INTERVAL = 100;
+
     private final DocumentNodeStore nodeStore;
 
     private final DocumentStore store;
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    private final AtomicInteger createCounter = new AtomicInteger();
+
+    private final Object cleanupLock = new Object();
+
     Checkpoints(DocumentNodeStore store) {
         this.nodeStore = store;
         this.store = store.getDocumentStore();
@@ -54,6 +65,8 @@ class Checkpoints {
 
     public Revision create(long lifetimeInMillis) {
         Revision r = nodeStore.getHeadRevision();
+        createCounter.getAndIncrement();
+        performCleanupIfRequired();
         UpdateOp op = new UpdateOp(ID, false);
         long endTime = nodeStore.getClock().getTime() + lifetimeInMillis;
         op.setMapEntry(PROP_CHECKPOINT, r, Long.toString(endTime));
@@ -61,10 +74,11 @@ class Checkpoints {
         return r;
     }
 
-
     /**
      * Returns the oldest valid checkpoint registered.
      *
+     * <p>It also performs cleanup of expired checkpoint
+     *
      * @return oldest valid checkpoint registered. Might return null if no 
valid
      * checkpoint found
      */
@@ -72,8 +86,7 @@ class Checkpoints {
     @CheckForNull
     public Revision getOldestRevisionToKeep() {
         //Get uncached doc
-        Document cdoc = store.find(Collection.SETTINGS, ID, 0);
-        SortedMap<Revision, String> checkpoints = (SortedMap<Revision, 
String>) cdoc.get(PROP_CHECKPOINT);
+        SortedMap<Revision, String> checkpoints = getCheckpoints();
 
         if(checkpoints == null){
             log.debug("No checkpoint registered so far");
@@ -103,6 +116,30 @@ class Checkpoints {
         return lastAliveRevision;
     }
 
+    @SuppressWarnings("unchecked")
+    @CheckForNull
+    private SortedMap<Revision, String> getCheckpoints() {
+        Document cdoc = store.find(Collection.SETTINGS, ID, 0);
+        return (SortedMap<Revision, String>) cdoc.get(PROP_CHECKPOINT);
+    }
+
+    int size(){
+        SortedMap<Revision, String> checkpoints = getCheckpoints();
+        return checkpoints == null ? 0 : checkpoints.size();
+    }
+
+    /**
+     * Triggers collection of expired checkpoints createCounter exceeds 
certain size
+     */
+    private void performCleanupIfRequired() {
+        if(createCounter.get() > CLEANUP_INTERVAL){
+            synchronized (cleanupLock){
+                getOldestRevisionToKeep();
+                createCounter.set(0);
+            }
+        }
+    }
+
     private void createIfNotExist() {
         if (store.find(Collection.SETTINGS, ID) == null) {
             UpdateOp updateOp = new UpdateOp(ID, true);

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=1583251&r1=1583250&r2=1583251&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
 Mon Mar 31 07:44:43 2014
@@ -18,11 +18,14 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.util.concurrent.TimeUnit;
+
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -53,6 +56,47 @@ public class CheckpointsTest {
     }
 
     @Test
+    public void testCheckpointPurgeByCount() throws Exception {
+        long expiryTime = TimeUnit.HOURS.toMillis(1);
+        Revision r1 = null;
+        for(int i = 0; i < Checkpoints.CLEANUP_INTERVAL; i++){
+            r1 = Revision.fromString(store.checkpoint(expiryTime));
+            store.setHeadRevision(Revision.newRevision(0));
+        }
+        assertEquals(r1, store.getCheckpoints().getOldestRevisionToKeep());
+        assertEquals(Checkpoints.CLEANUP_INTERVAL, 
store.getCheckpoints().size());
+
+        //Trigger expiry by forwarding the clock to future
+        clock.waitUntil(clock.getTime() + expiryTime);
+
+        //Now creating the next checkpoint should trigger
+        //cleanup
+        store.checkpoint(expiryTime);
+        assertEquals(1, store.getCheckpoints().size());
+    }
+
+    @Ignore("OAK-1648")
+    @Test
+    public void multipleCheckpointOnSameRevision() throws Exception{
+        long e1 = TimeUnit.HOURS.toMillis(1);
+        long e2 = TimeUnit.HOURS.toMillis(3);
+
+        //Create CP with higher expiry first and then one with
+        //lower expiry
+        Revision r2 = Revision.fromString(store.checkpoint(e2));
+        Revision r1 = Revision.fromString(store.checkpoint(e1));
+
+        //Head revision has not changed so revision must be same
+        assertEquals(r1,r2);
+
+        clock.waitUntil(clock.getTime() + e1 + 1);
+
+        //The older checkpoint was for greater duration so checkpoint
+        //must not be GC
+        assertEquals(r1, store.getCheckpoints().getOldestRevisionToKeep());
+    }
+
+    @Test
     public void testGetOldestRevisionToKeep() throws Exception {
         long et1 = 1000, et2 = et1 + 1000;
 


Reply via email to