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 md...@apache.org on 2012/05/02 15:47:38 UTC

svn commit: r1333048 - /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java

Author: mduerig
Date: Wed May  2 13:47:38 2012
New Revision: 1333048

URL: http://svn.apache.org/viewvc?rev=1333048&view=rev
Log:
OAK-80: Implement batched writing for KernelNodeStore
Add javadoc, minor refactoring

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java?rev=1333048&r1=1333047&r2=1333048&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java Wed May  2 13:47:38 2012
@@ -6,7 +6,6 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStore;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
@@ -18,9 +17,24 @@ import org.apache.jackrabbit.oak.util.Pr
 
 import java.util.Iterator;
 
+/**
+ * {@code NodeStore} implementations which supports batching changes
+ * to the content tree up until a certain limit is reached and write them
+ * down to the Microkernel in a single operation. The batch size is controlled
+ * through {@link #PURGE_LIMIT} which is the number of characters on a commit
+ * (i.e. jsop string).
+ */
 public class KernelNodeStore extends AbstractNodeStore {
+
+    /**
+     * Maximal size of size of a commit (number of characters of the corresponding
+     * jsop string). When the limit is reached, changes kept in memory are written
+     * back to the private branch in the Microkernel.
+     */
+    private static final int PURGE_LIMIT = 1024;  // TODO make configurable?
+
     /**
-     * The {@link org.apache.jackrabbit.mk.api.MicroKernel} instance used to store the content tree.
+     * The {@link MicroKernel} instance used to store the content tree.
      */
     private final MicroKernel kernel;
 
@@ -81,14 +95,25 @@ public class KernelNodeStore extends Abs
 
     //------------------------------------------------------------< internal >---
 
+    /**
+     * {@code NodeStateBuilderContext} keeps track of all changes to a
+     * {@code KernelNodeStateBuilder} which have not yet been written back to the
+     * Microkernel. It transforms the tree rooted at {@link #root} to reflect these
+     * changes and writes these changes back to the Microkernel when
+     * {@link KernelNodeStore#PURGE_LIMIT} is exceeded.
+     */
     class NodeStateBuilderContext {
-        private static final int PURGE_LIMIT = 1024;  // TODO make configurable?
 
+        /** Path of the root of the whole subtree */
         private final String path;
 
+        /** Root of the subtree */
         private NodeState root;
+
+        /** Current branch revision */
         private String revision;
 
+        /** Pending changes */
         private StringBuilder jsop = new StringBuilder();
 
         NodeStateBuilderContext(KernelNodeState root) {
@@ -97,10 +122,18 @@ public class KernelNodeStore extends Abs
             this.revision = root.getRevision();
         }
 
+        /**
+         * @return path of the root of the whole subtree
+         */
         String getPath() {
             return path;
         }
 
+        /**
+         * Get the node state located at {@code path}
+         * @param path  path relative to {@link #root}
+         * @return  node state at {@code path} or {@code null} if none.
+         */
         NodeState getNodeState(String path) {
             NodeState state = root;
             for (String name : PathUtils.elements(path)) {
@@ -110,24 +143,49 @@ public class KernelNodeStore extends Abs
             return state;
         }
 
+        /**
+         * Add a new, empty node state at {@code path}. The changes to the subtree
+         * are reflected in {@link #root}.
+         * @param relPath  path relative to {@link #root}. All but the last element
+         *                 must resolve to existing node states.
+         */
         void addNode(String relPath) {
             jsop.append("+\"").append(relPath).append("\":{}");
             root = addNode(root, EMPTY_STATE, PathUtils.elements(relPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Add a new node state at {@code path}. The changes to the subtree are reflected
+         * in {@link #root}.
+         * @param node     node state to add
+         * @param relPath  path relative to {@link #root}. All but the last element
+         *                 must resolve to existing node states.
+         */
         void addNode(NodeState node, String relPath) {
             buildJsop(relPath, node);
             root = addNode(root, node, PathUtils.elements(relPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Remove the node state at {@code path}. The changes to the subtree are reflected
+         * in {@link #root}.
+         * @param relPath  path relative to {@link #root}. All elements must resolve to
+         *                 existing node states.
+         */
         void removeNode(String relPath) {
             jsop.append("-\"").append(relPath).append('"');
             root = removeNode(root, PathUtils.elements(relPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Add a new property state. The changes to the subtree are reflected in {@link #root}.
+         * @param property     property state to add
+         * @param parentPath   path to the parent node state relative to {@link #root}.
+         *                     All elements must resolve to existing node states.
+         */
         void addProperty(PropertyState property, String parentPath) {
             String path = PathUtils.concat(parentPath, property.getName());
             String value = property.isArray()
@@ -138,6 +196,13 @@ public class KernelNodeStore extends Abs
             purgeOnLimit();
         }
 
+        /**
+         * Set an existing property state. The changes to the subtree are reflected in
+         * {@link #root}.
+         * @param property     property state to set
+         * @param parentPath   path to the parent node state relative to {@link #root}.
+         *                     All elements must resolve to existing node states.
+         */
         void setProperty(PropertyState property, String parentPath) {
             String path = PathUtils.concat(parentPath, property.getName());
             String value = property.isArray()
@@ -148,27 +213,53 @@ public class KernelNodeStore extends Abs
             purgeOnLimit();
         }
 
+        /**
+         * Remove an existing property state. The changes to the subtree are reflected in
+         * {@link #root}.
+         * @param relPath   path to the property state relative to {@link #root}. All
+         *                  elements must resolve to existing node states.
+         */
         void removeProperty(String relPath) {
             jsop.append("^\"").append(relPath).append("\":null");
             root = removeProperty(root, PathUtils.elements(relPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Move the node from {@code sourcePath} to {@code destPath}. The changes to
+         * the subtree are reflected in {@link #root}.
+         * @param sourcePath  path to the node to move. All elements must resolve to
+         *                    existing node states.
+         * @param destPath    path to the new node. All but the last element must resolve
+         *                    to existing node states.
+         */
         void moveNode(String sourcePath, String destPath) {
             jsop.append(">\"").append(sourcePath).append("\":\"").append(destPath).append('"');
-            NodeState moveNode = getChildNode(root, sourcePath);
+            NodeState moveNode = getChildNode(sourcePath);
             root = removeNode(root, PathUtils.elements(sourcePath).iterator());
             root = addNode(root, moveNode, PathUtils.elements(destPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Copy the node from {@code sourcePath} to {@code destPath}. The changes to
+         * the subtree are reflected in {@link #root}.
+         * @param sourcePath  path to the node to copy. All elements must resolve to
+         *                    existing node states.
+         * @param destPath    path to the new node. All but the last element must resolve
+         *                    to existing node states.
+         */
         void copyNode(String sourcePath, String destPath) {
             jsop.append("*\"").append(sourcePath).append("\":\"").append(destPath).append('"');
-            NodeState copyNode = getChildNode(root, sourcePath);
+            NodeState copyNode = getChildNode(sourcePath);
             root = addNode(root, copyNode, PathUtils.elements(destPath).iterator());
             purgeOnLimit();
         }
 
+        /**
+         * Merge back into trunk
+         * @throws CommitFailedException  if merging fails
+         */
         void applyPendingChanges() throws CommitFailedException {
             try {
                 purgePendingChanges();
@@ -182,12 +273,20 @@ public class KernelNodeStore extends Abs
 
         //------------------------------------------------------------< private >---
 
+        /**
+         * Purge all changes kept in memory to the private branch if
+         * {@link KernelNodeStore#PURGE_LIMIT} is exceeded.
+         * @see #purgePendingChanges()
+         */
         private void purgeOnLimit() {
             if (jsop.length() > PURGE_LIMIT) {
                 purgePendingChanges();
             }
         }
 
+        /**
+         * Purge all changes kept in memory to the private branch.
+         */
         private void purgePendingChanges() {
             if (revision == null) {
                 throw new IllegalStateException("Branch has been merged already");
@@ -200,6 +299,11 @@ public class KernelNodeStore extends Abs
             }
         }
 
+        /**
+         * Build a jsop statement for adding a node state at a given path.
+         * @param path        path where {@code nodeState} should be added.
+         * @param nodeState   node state to add.
+         */
         private void buildJsop(String path, NodeState nodeState) {
             jsop.append("+\"").append(path).append("\":{}");
 
@@ -218,6 +322,14 @@ public class KernelNodeStore extends Abs
             }
         }
 
+        /**
+         * Construct a new {@code NodeState} where {@code node} is added to
+         * {@code parent} at {@code path}.
+         * @param parent  parent where {@code node} should be added
+         * @param node    node state to add
+         * @param path    path from {@code parent} where {@code node} should be added
+         * @return  a new {@code NodeState} instance with the added node state.
+         */
         private NodeState addNode(NodeState parent, NodeState node, Iterator<String> path) {
             String name = path.next();
             if (path.hasNext()) {
@@ -228,6 +340,13 @@ public class KernelNodeStore extends Abs
             }
         }
 
+        /**
+         * Construct a new {@code NodeState} where the node state at {@code path} is
+         * removed from {@code parent}.
+         * @param parent  parent from which the node state should be removed
+         * @param path    path from {@code parent} for the node state to remove
+         * @return  a new {@code NodeState} instance with the remove node state.
+         */
         private NodeState removeNode(NodeState parent, Iterator<String> path) {
             String name = path.next();
             if (path.hasNext()) {
@@ -238,16 +357,34 @@ public class KernelNodeStore extends Abs
             }
         }
 
-        private NodeState addProperty(NodeState parent, PropertyState added, Iterator<String> parentPath) {
+        /**
+         * Construct a new {@code NodeState} where {@code property} is added to
+         * {@code parent} at {@code parentPath}.
+         * @param parent      parent where {@code node} should be added
+         * @param property    property state to add
+         * @param parentPath  path from {@code parent} where {@code property} should be
+         *                    added
+         * @return  a new {@code NodeState} instance with the added property state.
+         */
+        private NodeState addProperty(NodeState parent, PropertyState property, Iterator<String> parentPath) {
             if (parentPath.hasNext()) {
                 String name = parentPath.next();
-                return setChildNode(parent, name, addProperty(parent.getChildNode(name), added, parentPath));
+                return setChildNode(parent, name, addProperty(parent.getChildNode(name), property, parentPath));
             }
             else {
-                return addChildProperty(parent, added);
+                return addChildProperty(parent, property);
             }
         }
 
+        /**
+         * Construct a new {@code NodeState} where {@code property} is set to
+         * {@code parent} at {@code parentPath}.
+         * @param parent      parent where {@code node} should be set
+         * @param property    property state to set
+         * @param parentPath  path from {@code parent} where {@code property} should be
+         *                    set
+         * @return  a new {@code NodeState} instance with the new property state.
+         */
         private NodeState setProperty(NodeState parent, PropertyState property, Iterator<String> parentPath) {
             if (parentPath.hasNext()) {
                 String name = parentPath.next();
@@ -258,6 +395,13 @@ public class KernelNodeStore extends Abs
             }
         }
 
+        /**
+         * Construct a new {@code NodeState} where the property state at {@code path} is
+         * removed from {@code parent}.
+         * @param parent  parent from which the property state should be removed
+         * @param path    path from {@code parent} for the property state to remove
+         * @return  a new {@code NodeState} instance with the remove property state.
+         */
         private NodeState removeProperty(NodeState parent, Iterator<String> path) {
             String name = path.next();
             if (path.hasNext()) {
@@ -268,7 +412,13 @@ public class KernelNodeStore extends Abs
             }
         }
 
-        private NodeState getChildNode(NodeState state, String relPath) {
+        /**
+         * Get the node state located at {@code relPath} from {@link #root}.
+         * @param relPath  relative path
+         * @return  child node at {@code relPath} or {@code null} if none.
+         */
+        private NodeState getChildNode(String relPath) {
+            NodeState state = root;
             for (String name : PathUtils.elements(relPath)) {
                 state = state.getChildNode(name);
             }
@@ -317,20 +467,14 @@ public class KernelNodeStore extends Abs
             }
         };
 
-        private ChildNodeEntry createCNE(final String name, final NodeState state) {
-            return new AbstractChildNodeEntry() {
-                @Override
-                public String getName() {
-                    return name;
-                }
-
-                @Override
-                public NodeState getNodeState() {
-                    return state;
-                }
-            };
-        }
-
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with {@code node} added
+         * as new child with name {@code childName}.
+         * @param parent
+         * @param childName
+         * @param node
+         * @return
+         */
         private NodeState addChildNode(final NodeState parent, final String childName, final NodeState node) {
             return new AbstractNodeState() {
                 @Override
@@ -374,7 +518,7 @@ public class KernelNodeStore extends Abs
                             public Iterator<ChildNodeEntry> iterator() {
                                 return Iterators.chain(
                                     parent.getChildNodeEntries(offset, count).iterator(),
-                                    Iterators.singleton(createCNE(childName, node)));
+                                    Iterators.singleton(new KernelChildNodeEntry(childName, node)));
                             }
                         };
                     }
@@ -386,6 +530,14 @@ public class KernelNodeStore extends Abs
             };
         }
 
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with child node state
+         * {@code childName} replaced with {@code node}.
+         * @param parent
+         * @param childName
+         * @param node
+         * @return
+         */
         private NodeState setChildNode(final NodeState parent, final String childName, final NodeState node) {
             return new AbstractNodeState() {
                 @Override
@@ -423,7 +575,7 @@ public class KernelNodeStore extends Abs
                                     @Override
                                     public ChildNodeEntry apply(ChildNodeEntry cne) {
                                         return childName.equals(cne.getName())
-                                                ? createCNE(childName, node)
+                                                ? new KernelChildNodeEntry(childName, node)
                                                 : cne;
                                     }
                                 });
@@ -433,6 +585,13 @@ public class KernelNodeStore extends Abs
             };
         }
 
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with child node state
+         * {@code childName} removed.
+         * @param parent
+         * @param childName
+         * @return
+         */
         private NodeState removeChildNode(final NodeState parent, final String childName) {
             return new AbstractNodeState() {
                 @Override
@@ -479,6 +638,13 @@ public class KernelNodeStore extends Abs
             };
         }
 
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with {@code property}
+         * added.
+         * @param parent
+         * @param property
+         * @return
+         */
         private NodeState addChildProperty(final NodeState parent, final PropertyState property) {
             return new AbstractNodeState() {
                 @Override
@@ -522,6 +688,13 @@ public class KernelNodeStore extends Abs
             };
         }
 
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with {@code property}
+         * replaced.
+         * @param parent
+         * @param property
+         * @return
+         */
         private NodeState setChildProperty(final NodeState parent, final PropertyState property) {
             return new AbstractNodeState() {
                 @Override
@@ -572,6 +745,13 @@ public class KernelNodeStore extends Abs
             };
         }
 
+        /**
+         * Construct a new {@code NodeState} from {@code parent} with {@code propertyName}
+         * removed.
+         * @param parent
+         * @param propertyName
+         * @return
+         */
         private NodeState removeChildProperty(final NodeState parent, final String propertyName) {
             return new AbstractNodeState() {
                 @Override