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