Author: mreutegg
Date: Tue Feb 17 14:06:00 2015
New Revision: 1660383

URL: http://svn.apache.org/r1660383
Log:
OAK-2528: Entries in _commitRoot not purged

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java?rev=1660383&r1=1660382&r2=1660383&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
 Tue Feb 17 14:06:00 2015
@@ -21,7 +21,6 @@ package org.apache.jackrabbit.oak.plugin
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
@@ -65,6 +64,7 @@ import static org.apache.jackrabbit.oak.
 class SplitOperations {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(SplitOperations.class);
+    private static final int GARBAGE_LIMIT = 
Integer.getInteger("oak.documentMK.garbage.limit", 10000);
     private static final DocumentStore STORE = new MemoryDocumentStore();
 
     private final NodeDocument doc;
@@ -75,7 +75,9 @@ class SplitOperations {
     private Revision low;
     private int numValues;
     private Map<String, NavigableMap<Revision, String>> committedChanges;
+    private Set<Revision> changes;
     private Map<String, Set<Revision>> garbage;
+    private int garbageCount = 0;
     private Set<Revision> mostRecentRevs;
     private Set<Revision> splitRevs;
     private List<UpdateOp> splitOps;
@@ -119,7 +121,10 @@ class SplitOperations {
         mostRecentRevs = Sets.newHashSet();
         splitRevs = Sets.newHashSet();
         garbage = Maps.newHashMap();
-        committedChanges = getCommittedLocalChanges();
+        changes = Sets.newHashSet();
+        committedChanges = Maps.newHashMap();
+        
+        collectLocalChanges(committedChanges, changes);
 
         // revisions of the most recent committed changes on this document
         // these are kept in the main document. _revisions and _commitRoot
@@ -215,9 +220,15 @@ class SplitOperations {
         NavigableMap<Revision, String> commitRoot =
                 new TreeMap<Revision, String>(context.getRevisionComparator());
         for (Map.Entry<Revision, String> entry : 
doc.getLocalCommitRoot().entrySet()) {
-            if (splitRevs.contains(entry.getKey())) {
-                commitRoot.put(entry.getKey(), entry.getValue());
+            Revision r = entry.getKey();
+            if (splitRevs.contains(r)) {
+                commitRoot.put(r, entry.getValue());
                 numValues++;
+            } else if (r.getClusterId() == context.getClusterId() 
+                    && !changes.contains(r)) {
+                // OAK-2528: _commitRoot entry without associated
+                // change -> consider as garbage
+                addGarbage(r, COMMIT_ROOT);
             }
         }
         committedChanges.put(COMMIT_ROOT, commitRoot);
@@ -350,15 +361,15 @@ class SplitOperations {
     }
 
     /**
-     * Returns a map of all local property changes committed by the current
+     * Collects all local property changes committed by the current
      * cluster node.
      *
-     * @return local changes committed by the current cluster node.
+     * @param committedLocally local changes committed by the current cluster 
node.
+     * @param changes all revisions of local changes (committed and 
uncommitted).
      */
-    @Nonnull
-    private Map<String, NavigableMap<Revision, String>> 
getCommittedLocalChanges() {
-        Map<String, NavigableMap<Revision, String>> committedLocally
-                = new HashMap<String, NavigableMap<Revision, String>>();
+    private void collectLocalChanges(
+            Map<String, NavigableMap<Revision, String>> committedLocally,
+            Set<Revision> changes) {
         for (String property : filter(doc.keySet(), PROPERTY_OR_DELETED)) {
             NavigableMap<Revision, String> splitMap
                     = new TreeMap<Revision, 
String>(context.getRevisionComparator());
@@ -370,6 +381,7 @@ class SplitOperations {
                 if (rev.getClusterId() != context.getClusterId()) {
                     continue;
                 }
+                changes.add(rev);
                 if (doc.isCommitted(rev)) {
                     splitMap.put(rev, entry.getValue());
                 } else if (isGarbage(rev)) {
@@ -377,7 +389,6 @@ class SplitOperations {
                 }
             }
         }
-        return committedLocally;
     }
     
     private boolean isGarbage(Revision rev) {
@@ -392,12 +403,17 @@ class SplitOperations {
     }
     
     private void addGarbage(Revision rev, String property) {
+        if (garbageCount > GARBAGE_LIMIT) {
+            return;
+        }
         Set<Revision> revisions = garbage.get(property);
         if (revisions == null) {
             revisions = Sets.newHashSet();
             garbage.put(property, revisions);
         }
-        revisions.add(rev);
+        if (revisions.add(rev)) {
+            garbageCount++;
+        }
     }
 
     private void disconnectStalePrevDocs() {
@@ -444,8 +460,10 @@ class SplitOperations {
         for (Map.Entry<String, Set<Revision>> entry : garbage.entrySet()) {
             for (Revision r : entry.getValue()) {
                 main.removeMapEntry(entry.getKey(), r);
-                NodeDocument.removeCommitRoot(main, r);
-                NodeDocument.removeRevision(main, r);
+                if (PROPERTY_OR_DELETED.apply(entry.getKey())) {
+                    NodeDocument.removeCommitRoot(main, r);
+                    NodeDocument.removeRevision(main, r);
+                }
             }
         }
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java?rev=1660383&r1=1660382&r2=1660383&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
 Tue Feb 17 14:06:00 2015
@@ -28,6 +28,8 @@ import java.util.TreeSet;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterators;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore;
 import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
@@ -35,6 +37,7 @@ import org.apache.jackrabbit.oak.plugins
 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.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.junit.Test;
 
@@ -709,7 +712,31 @@ public class DocumentSplitTest extends B
 
         ns.dispose();
     }
+    
+    // OAK-2528
+    @Test
+    public void commitRootForChildrenFlag() throws Exception {
+        DocumentStore store = mk.getDocumentStore();
+        DocumentNodeStore ns = mk.getNodeStore();
+        
+        for (int i = 0; i < NUM_REVS_THRESHOLD * 2; i++) {
+            NodeBuilder builder = ns.getRoot().builder();
+            builder.child("test").child("child-" + i);
+            merge(ns, builder);
+        }
+        
+        ns.runBackgroundOperations();
+        
+        NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test"));
+        assertNotNull(doc);
+        assertTrue(doc.getLocalCommitRoot().size() < NUM_REVS_THRESHOLD);
+    }
 
+    private static NodeState merge(NodeStore store, NodeBuilder root)
+            throws CommitFailedException {
+        return store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+    }
+    
     private void syncMKs(List<DocumentMK> mks, int idx) {
         mks.get(idx).runBackgroundOperations();
         for (int i = 0; i < mks.size(); i++) {


Reply via email to