You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2014/04/14 22:42:45 UTC

svn commit: r1587312 - in /jackrabbit/oak/branches/1.0: ./ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/

Author: mreutegg
Date: Mon Apr 14 20:42:45 2014
New Revision: 1587312

URL: http://svn.apache.org/r1587312
Log:
OAK-1729: DocumentNodeStore revision GC removes intermediate docs

Modified:
    jackrabbit/oak/branches/1.0/   (props changed)
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java

Propchange: jackrabbit/oak/branches/1.0/
------------------------------------------------------------------------------
  Merged /jackrabbit/oak/trunk:r1587224

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java?rev=1587312&r1=1587311&r2=1587312&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java Mon Apr 14 20:42:45 2014
@@ -51,10 +51,12 @@ import org.slf4j.LoggerFactory;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
+import static java.util.Collections.disjoint;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
 
@@ -1339,7 +1341,7 @@ public final class NodeDocument extends 
         setSplitDocMaxRev(old, maxRev);
 
         SplitDocType type = SplitDocType.DEFAULT;
-        if(!mainDoc.hasChildren()){
+        if(!mainDoc.hasChildren() && !referencesOldDocAfterSplit(mainDoc, oldDoc)){
             type = SplitDocType.DEFAULT_NO_CHILD;
         } else if (oldDoc.getLocalRevisions().isEmpty()){
             type = SplitDocType.PROP_COMMIT_ONLY;
@@ -1354,6 +1356,31 @@ public final class NodeDocument extends 
     }
 
     /**
+     * Checks if the main document has changes referencing {@code oldDoc} after
+     * the split.
+     *
+     * @param mainDoc the main document before the split.
+     * @param oldDoc  the old document created by the split.
+     * @return {@code true} if the main document contains references to the
+     *         old document after the split; {@code false} otherwise.
+     */
+    private static boolean referencesOldDocAfterSplit(NodeDocument mainDoc,
+                                                      NodeDocument oldDoc) {
+        Set<Revision> revs = oldDoc.getLocalRevisions().keySet();
+        for (String property : mainDoc.data.keySet()) {
+            if (IGNORE_ON_SPLIT.contains(property)) {
+                continue;
+            }
+            Set<Revision> changes = Sets.newHashSet(mainDoc.getLocalMap(property).keySet());
+            changes.removeAll(oldDoc.getLocalMap(property).keySet());
+            if (!disjoint(changes, revs)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Set various properties for intermediate split document
      *
      * @param intermediate updateOp of the intermediate doc getting created

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java?rev=1587312&r1=1587311&r2=1587312&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java Mon Apr 14 20:42:45 2014
@@ -46,8 +46,7 @@ public class VersionGarbageCollector {
      */
     private static final Set<NodeDocument.SplitDocType> GC_TYPES = EnumSet.of(
             NodeDocument.SplitDocType.DEFAULT_NO_CHILD,
-            NodeDocument.SplitDocType.PROP_COMMIT_ONLY,
-            NodeDocument.SplitDocType.INTERMEDIATE);
+            NodeDocument.SplitDocType.PROP_COMMIT_ONLY);
 
 
     VersionGarbageCollector(DocumentNodeStore nodeStore) {

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java?rev=1587312&r1=1587311&r2=1587312&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java Mon Apr 14 20:42:45 2014
@@ -51,8 +51,6 @@ public abstract class DocumentStoreFixtu
 
     public static class MemoryFixture extends DocumentStoreFixture {
 
-        DocumentStore ds = new MemoryDocumentStore();
-
         @Override
         public String getName() {
             return "Memory";
@@ -60,7 +58,7 @@ public abstract class DocumentStoreFixtu
 
         @Override
         public DocumentStore createDocumentStore() {
-            return ds;
+            return new MemoryDocumentStore();
         }
     }
 
@@ -75,7 +73,7 @@ public abstract class DocumentStoreFixtu
                 DataSource datas = RDBDataSourceFactory.forJdbcUrl(url, username, passwd);
                 this.ds = new RDBDocumentStore(datas, new DocumentMK.Builder());
             } catch (Exception ex) {
-                LOG.info("Database instance not available at " + url + ", skipping tests...", ex);
+                LOG.info("Database instance not available at " + url + ", skipping tests...");
             }
         }
 

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java?rev=1587312&r1=1587311&r2=1587312&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java Mon Apr 14 20:42:45 2014
@@ -20,15 +20,19 @@
 package org.apache.jackrabbit.oak.plugins.document;
 
 import java.io.IOException;
-import java.util.*;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import static org.apache.jackrabbit.oak.plugins.document.Collection.*;
+import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
+import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.NUM_REVS_THRESHOLD;
+import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.PREV_SPLIT_FACTOR;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.SplitDocType;
 import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -206,6 +210,53 @@ public class VersionGarbageCollectorTest
         //assertTrue(ImmutableList.copyOf(getDoc("/test2/foo").getAllPreviousDocs()).isEmpty());
     }
 
+    // OAK-1729
+    @Test
+    public void gcIntermediateDocs() throws Exception {
+        long maxAge = 1; //hrs
+        long delta = TimeUnit.MINUTES.toMillis(10);
+
+        NodeBuilder b1 = store.getRoot().builder();
+        // adding the foo node will cause the commit root to be placed
+        // on the rood document, because the children flag is set on the
+        // root document
+        b1.child("foo");
+        store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        // adding test afterwards will use the new test document as the
+        // commit root. this what we want for the test.
+        b1.child("test");
+        store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        assertTrue(!getDoc("/test").getLocalRevisions().isEmpty());
+
+        for (int i = 0; i < PREV_SPLIT_FACTOR + 1; i++) {
+            for (int j = 0; j < NUM_REVS_THRESHOLD; j++) {
+                b1 = store.getRoot().builder();
+                b1.child("test").setProperty("prop", i * NUM_REVS_THRESHOLD + j);
+                store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+            }
+            store.runBackgroundOperations();
+        }
+
+        Map<Revision, Range> prevRanges = getDoc("/test").getPreviousRanges();
+        boolean hasIntermediateDoc = false;
+        for (Map.Entry<Revision, Range> entry : prevRanges.entrySet()) {
+            if (entry.getValue().getHeight() > 0) {
+                hasIntermediateDoc = true;
+                break;
+            }
+        }
+        assertTrue("Test data does not have intermediate previous docs",
+                hasIntermediateDoc);
+
+        clock.waitUntil(clock.getTime() + TimeUnit.HOURS.toMillis(maxAge) + delta);
+        VersionGCStats stats = gc.gc(maxAge, TimeUnit.HOURS);
+        assertEquals(10, stats.splitDocGCCount);
+
+        DocumentNodeState test = getDoc("/test").getNodeAtRevision(
+                store, store.getHeadRevision(), null);
+        assertNotNull(test);
+    }
+
     private NodeDocument getDoc(String path){
         return store.getDocumentStore().find(NODES, Utils.getIdFromPath(path), 0);
     }