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;