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/09/15 12:35:23 UTC

svn commit: r1624993 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document: Branch.java DocumentNodeStore.java LastRevTracker.java UnmergedBranches.java UnsavedModifications.java

Author: mreutegg
Date: Mon Sep 15 10:35:23 2014
New Revision: 1624993

URL: http://svn.apache.org/r1624993
Log:
OAK-1768: DocumentNodeBuilder.setChildNode() runs OOM with large tree

Work in progress:
- Introduce different types of branch commits (regular and rebase commit) to reduce memory usage of a rebase
- Better hide UnsavedModifications as an implementation detail

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java?rev=1624993&r1=1624992&r2=1624993&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java Mon Sep 15 10:35:23 2014
@@ -18,13 +18,21 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
 
+import java.util.NavigableMap;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.concurrent.ConcurrentSkipListMap;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
 /**
  * Contains commit information about a branch and its base revision.
  */
@@ -54,7 +62,8 @@ class Branch {
         this.base = base;
         this.commits = new ConcurrentSkipListMap<Revision, BranchCommit>(commits.comparator());
         for (Revision r : commits) {
-            this.commits.put(r.asBranchRevision(), new BranchCommit(base));
+            this.commits.put(r.asBranchRevision(),
+                    new BranchCommitImpl(base, r.asBranchRevision()));
         }
     }
 
@@ -97,12 +106,7 @@ class Branch {
         checkArgument(!checkNotNull(base).isBranch(), "Not a trunk revision: %s", base);
         Revision last = commits.lastKey();
         checkArgument(commits.comparator().compare(head, last) > 0);
-        BranchCommit bc = new BranchCommit(base);
-        // set all previously touched paths as modified
-        for (BranchCommit c : commits.values()) {
-            c.getModifications().applyTo(bc.getModifications(), head);
-        }
-        commits.put(head, bc);
+        commits.put(head, new RebaseCommit(base, head, commits));
     }
 
     /**
@@ -115,7 +119,7 @@ class Branch {
         checkArgument(checkNotNull(r).isBranch(), "Not a branch revision: %s", r);
         Revision last = commits.lastKey();
         checkArgument(commits.comparator().compare(r, last) > 0);
-        commits.put(r, new BranchCommit(commits.get(last).getBase()));
+        commits.put(r, new BranchCommitImpl(commits.get(last).getBase(), r));
     }
 
     /**
@@ -145,6 +149,18 @@ class Branch {
     }
 
     /**
+     * Returns the branch commit with the given or {@code null} if it does not
+     * exist.
+     *
+     * @param r the revision of a commit.
+     * @return the branch commit or {@code null} if it doesn't exist.
+     */
+    @CheckForNull
+    BranchCommit getCommit(@Nonnull Revision r) {
+        return commits.get(checkNotNull(r).asBranchRevision());
+    }
+
+    /**
      * Removes the commit with the given revision <code>r</code>. Does nothing
      * if there is no such commit.
      *
@@ -157,25 +173,6 @@ class Branch {
     }
 
     /**
-     * Gets the unsaved modifications for the given branch commit revision.
-     *
-     * @param r a branch commit revision.
-     * @return the unsaved modification for the given branch commit.
-     * @throws IllegalArgumentException r is not a branch revision or if there
-     *                                  is no commit with the given revision.
-     */
-    @Nonnull
-    public UnsavedModifications getModifications(@Nonnull Revision r) {
-        checkArgument(checkNotNull(r).isBranch(), "Not a branch revision: %s", r);
-        BranchCommit c = commits.get(r);
-        if (c == null) {
-            throw new IllegalArgumentException(
-                    "Revision " + r + " is not a commit in this branch");
-        }
-        return c.getModifications();
-    }
-
-    /**
      * Applies all unsaved modification of this branch to the given collection
      * of unsaved trunk modifications with the given merge commit revision.
      *
@@ -186,7 +183,7 @@ class Branch {
                         @Nonnull Revision mergeCommit) {
         checkNotNull(trunk);
         for (BranchCommit c : commits.values()) {
-            c.getModifications().applyTo(trunk, mergeCommit);
+            c.applyTo(trunk, mergeCommit);
         }
     }
 
@@ -208,9 +205,8 @@ class Branch {
                 continue;
             }
             BranchCommit c = commits.get(r);
-            Revision modRevision = c.getModifications().get(path);
-            if (modRevision != null) {
-                return modRevision;
+            if (c.isModified(path)) {
+                return r;
             }
         }
         return null;
@@ -230,21 +226,107 @@ class Branch {
     /**
      * Information about a commit within a branch.
      */
-    private static final class BranchCommit {
+    abstract static class BranchCommit implements LastRevTracker {
 
-        private final UnsavedModifications modifications = new UnsavedModifications();
-        private final Revision base;
+        protected final Revision base;
+        protected final Revision commit;
 
-        BranchCommit(Revision base) {
+        BranchCommit(Revision base, Revision commit) {
             this.base = base;
+            this.commit = commit;
         }
 
         Revision getBase() {
             return base;
         }
 
-        UnsavedModifications getModifications() {
+        abstract void applyTo(UnsavedModifications trunk, Revision commit);
+
+        abstract boolean isModified(String path);
+
+        abstract Iterable<String> getModifiedPaths();
+    }
+
+    /**
+     * Implements a regular branch commit.
+     */
+    private static class BranchCommitImpl extends BranchCommit {
+
+        private final Set<String> modifications = Sets.newHashSet();
+
+        BranchCommitImpl(Revision base, Revision commit) {
+            super(base, commit);
+        }
+
+        @Override
+        void applyTo(UnsavedModifications trunk, Revision commit) {
+            for (String p : modifications) {
+                trunk.put(p, commit);
+            }
+        }
+
+        @Override
+        boolean isModified(String path) { // TODO: rather pass NodeDocument?
+            return modifications.contains(path);
+        }
+
+        @Override
+        Iterable<String> getModifiedPaths() {
             return modifications;
         }
+
+        //------------------< LastRevTracker >----------------------------------
+
+        @Override
+        public void track(String path) {
+            modifications.add(path);
+        }
+    }
+
+    static class RebaseCommit extends BranchCommit {
+
+        private final NavigableMap<Revision, BranchCommit> previous;
+
+        RebaseCommit(Revision base, Revision commit,
+                     NavigableMap<Revision, BranchCommit> previous) {
+            super(base, commit);
+            this.previous = Maps.newTreeMap(previous);
+        }
+
+        @Override
+        void applyTo(UnsavedModifications trunk, Revision commit) {
+            for (BranchCommit c : previous.values()) {
+                c.applyTo(trunk, commit);
+            }
+        }
+
+        @Override
+        boolean isModified(String path) {
+            for (BranchCommit c : previous.values()) {
+                if (c.isModified(path)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        Iterable<String> getModifiedPaths() {
+            Iterable<Iterable<String>> paths = transform(previous.values(),
+                    new Function<BranchCommit, Iterable<String>>() {
+                @Override
+                public Iterable<String> apply(BranchCommit branchCommit) {
+                    return branchCommit.getModifiedPaths();
+                }
+            });
+            return Iterables.concat(paths);
+        }
+
+        //------------------< LastRevTracker >----------------------------------
+
+        @Override
+        public void track(String path) {
+            throw new UnsupportedOperationException("RebaseCommit is read-only");
+        }
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1624993&r1=1624992&r2=1624993&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Mon Sep 15 10:35:23 2014
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.plugin
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
+import static org.apache.jackrabbit.oak.plugins.document.Branch.BranchCommit;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.FAST_DIFF;
 import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.MANY_CHILDREN_THRESHOLD;
@@ -912,19 +913,19 @@ public final class DocumentNodeStore
                              boolean isBranchCommit, List<String> added,
                              List<String> removed, List<String> changed,
                              DiffCache.Entry cacheEntry) {
-        UnsavedModifications unsaved = unsavedLastRevisions;
+        LastRevTracker tracker = createTracker(rev);
         if (disableBranches) {
             if (pendingLastRev) {
-                unsaved.put(path, rev);
+                tracker.track(path);
             }
         } else {
             if (isBranchCommit) {
                 Revision branchRev = rev.asBranchRevision();
-                unsaved = branches.getBranch(branchRev).getModifications(branchRev);
+                tracker = branches.getBranchCommit(branchRev);
             }
             if (isBranchCommit || pendingLastRev) {
                 // write back _lastRev with background thread
-                unsaved.put(path, rev);
+                tracker.track(path);
             }
         }
         if (isNew) {
@@ -1497,6 +1498,21 @@ public final class DocumentNodeStore
 
     //-----------------------------< internal >---------------------------------
 
+    /**
+     * Creates a tracker for the given commit revision.
+     *
+     * @param r a commit revision.
+     * @return a _lastRev tracker for the given commit revision.
+     */
+    private LastRevTracker createTracker(final @Nonnull Revision r) {
+        return new LastRevTracker() {
+            @Override
+            public void track(String path) {
+                unsavedLastRevisions.put(path, r);
+            }
+        };
+    }
+
     private static void diffProperties(DocumentNodeState from,
                                        DocumentNodeState to,
                                        JsopWriter w) {
@@ -1561,12 +1577,12 @@ public final class DocumentNodeStore
         }
         // also consider nodes with not yet stored modifications (OAK-1107)
         Revision minRev = new Revision(minTimestamp, 0, getClusterId());
-        addPathsForDiff(path, paths, getPendingModifications(), minRev);
+        addPathsForDiff(path, paths, getPendingModifications().getPaths(minRev));
         for (Revision r : new Revision[]{fromRev, toRev}) {
             if (r.isBranch()) {
-                Branch b = getBranches().getBranch(r);
-                if (b != null) {
-                    addPathsForDiff(path, paths, b.getModifications(r), r);
+                BranchCommit c = getBranches().getBranchCommit(r);
+                if (c != null) {
+                    addPathsForDiff(path, paths, c.getModifiedPaths());
                 }
             }
         }
@@ -1601,9 +1617,8 @@ public final class DocumentNodeStore
 
     private static void addPathsForDiff(String path,
                                         Set<String> paths,
-                                        UnsavedModifications pending,
-                                        Revision minRev) {
-        for (String p : pending.getPaths(minRev)) {
+                                        Iterable<String> modified) {
+        for (String p : modified) {
             if (PathUtils.denotesRoot(p)) {
                 continue;
             }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java?rev=1624993&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java Mon Sep 15 10:35:23 2014
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+/**
+ * An implementation of this interface receives callbacks about paths
+ * that need an update of the _lastRev field on documents.
+ */
+public interface LastRevTracker {
+
+    /**
+     * Called when a document needs an update of the _lastRev field.
+     *
+     * @param path the path of the document to update.
+     */
+    public void track(String path);
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java?rev=1624993&r1=1624992&r2=1624993&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java Mon Sep 15 10:35:23 2014
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.document.Branch.BranchCommit;
 
 import java.util.Comparator;
 import java.util.List;
@@ -120,6 +121,24 @@ class UnmergedBranches {
     }
 
     /**
+     * Returns the branch commit with the given revision or {@code null} if
+     * it doesn't exists.
+     *
+     * @param r a revision.
+     * @return the branch commit or {@code null} if it doesn't exist.
+     */
+    @CheckForNull
+    BranchCommit getBranchCommit(@Nonnull Revision r) {
+        for (Branch b : branches) {
+            BranchCommit c = b.getCommit(r);
+            if (c != null) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Removes the given branch.
      * @param b the branch to remove.
      */

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java?rev=1624993&r1=1624992&r2=1624993&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java Mon Sep 15 10:35:23 2014
@@ -95,21 +95,6 @@ class UnsavedModifications {
     }
 
     /**
-     * Applies all modifications from this instance to the <code>other</code>.
-     * A modification is only applied if there is no modification in other
-     * for a given path or if the other modification is earlier than the
-     * {@code commit} revision.
-     *
-     * @param other the other <code>UnsavedModifications</code>.
-     * @param commit the commit revision.
-     */
-    public void applyTo(UnsavedModifications other, Revision commit) {
-        for (Map.Entry<String, Revision> entry : map.entrySet()) {
-            other.put(entry.getKey(), commit);
-        }
-    }
-
-    /**
      * Returns all paths of nodes with modifications at the start revision
      * (inclusive) or later.
      *