Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 40CCFC4DC for ; Wed, 2 May 2012 13:48:01 +0000 (UTC) Received: (qmail 97241 invoked by uid 500); 2 May 2012 13:48:01 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 97215 invoked by uid 500); 2 May 2012 13:48:01 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-commits@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 97207 invoked by uid 99); 2 May 2012 13:48:01 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 May 2012 13:48:01 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 May 2012 13:47:58 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id CB6AB238890B; Wed, 2 May 2012 13:47:38 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1333048 - /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java Date: Wed, 02 May 2012 13:47:38 -0000 To: oak-commits@jackrabbit.apache.org From: mduerig@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120502134738.CB6AB238890B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 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 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 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 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 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 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 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