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 ju...@apache.org on 2012/09/05 21:08:39 UTC
svn commit: r1381301 - in /jackrabbit/oak/trunk:
oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/
oak-core/src/main/java/org/apache/jackrabbit/oak/query/
oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/
oak-mk/src/main/java/org/apa...
Author: jukka
Date: Wed Sep 5 19:08:38 2012
New Revision: 1381301
URL: http://svn.apache.org/viewvc?rev=1381301&view=rev
Log:
OAK-167: Caching NodeStore implementation
Introduce a Cache of KernelNodeStates
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java
jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java Wed Sep 5 19:08:38 2012
@@ -26,22 +26,27 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
import javax.annotation.Nonnull;
import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.mk.json.JsopReader;
import org.apache.jackrabbit.mk.json.JsopTokenizer;
import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
import org.apache.jackrabbit.oak.plugins.memory.MultiPropertyState;
import org.apache.jackrabbit.oak.plugins.memory.SinglePropertyState;
+import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -68,8 +73,9 @@ public final class KernelNodeState exten
private String hash;
- // TODO: WeakReference?
- private Map<String, NodeState> childNodes;
+ private Map<String, String> childPaths;
+
+ private final LoadingCache<String, KernelNodeState> cache;
/**
* Create a new instance of this class representing the node at the
@@ -80,7 +86,9 @@ public final class KernelNodeState exten
* @param path
* @param revision
*/
- public KernelNodeState(MicroKernel kernel, String path, String revision) {
+ public KernelNodeState(
+ MicroKernel kernel, String path, String revision,
+ LoadingCache<String, KernelNodeState> cache) {
assert kernel != null;
assert path != null;
assert revision != null;
@@ -88,6 +96,7 @@ public final class KernelNodeState exten
this.kernel = kernel;
this.path = path;
this.revision = revision;
+ this.cache = cache;
}
private synchronized void init() {
@@ -99,7 +108,7 @@ public final class KernelNodeState exten
JsopReader reader = new JsopTokenizer(json);
reader.read('{');
properties = new LinkedHashMap<String, PropertyState>();
- childNodes = new LinkedHashMap<String, NodeState>();
+ childPaths = new LinkedHashMap<String, String>();
do {
String name = reader.readString();
reader.read(':');
@@ -114,7 +123,7 @@ public final class KernelNodeState exten
if ("/".equals(path)) {
childPath = '/' + name;
}
- childNodes.put(name, new KernelNodeState(kernel, childPath, revision));
+ childPaths.put(name, childPath);
} else if (reader.matches('[')) {
List<CoreValue> values = listFromJsopReader(reader, kernel);
properties.put(name, new MultiPropertyState(name, values));
@@ -126,8 +135,8 @@ public final class KernelNodeState exten
reader.read('}');
reader.read(JsopReader.END);
// optimize for empty childNodes
- if (childNodes.isEmpty()) {
- childNodes = Collections.emptyMap();
+ if (childPaths.isEmpty()) {
+ childPaths = Collections.emptyMap();
}
}
}
@@ -159,25 +168,31 @@ public final class KernelNodeState exten
@Override
public NodeState getChildNode(String name) {
init();
- NodeState child = childNodes.get(name);
- if (child == null && childNodeCount > MAX_CHILD_NODE_NAMES) {
- String childPath = getChildPath(name);
- if (kernel.nodeExists(childPath, revision)) {
- child = new KernelNodeState(kernel, childPath, revision);
+ String childPath = childPaths.get(name);
+ if (childPath == null && childNodeCount > MAX_CHILD_NODE_NAMES) {
+ String path = getChildPath(name);
+ if (kernel.nodeExists(path, revision)) {
+ childPath = path;
}
}
- return child;
+ if (childPath == null) {
+ return null;
+ }
+ try {
+ return cache.get(revision + childPath);
+ } catch (ExecutionException e) {
+ throw new MicroKernelException(e);
+ }
}
@Override
public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
init();
- Iterable<ChildNodeEntry> iterable =
- MemoryChildNodeEntry.iterable(childNodes.entrySet());
- if (childNodeCount > childNodes.size()) {
+ Iterable<ChildNodeEntry> iterable = iterable(childPaths.entrySet());
+ if (childNodeCount > childPaths.size()) {
List<Iterable<ChildNodeEntry>> iterables = Lists.newArrayList();
iterables.add(iterable);
- long offset = childNodes.size();
+ long offset = childPaths.size();
while (offset < childNodeCount) {
iterables.add(getChildNodeEntries(offset, MAX_CHILD_NODE_NAMES));
offset += MAX_CHILD_NODE_NAMES;
@@ -283,9 +298,7 @@ public final class KernelNodeState exten
if (reader.matches('{')) {
reader.read('}');
String childPath = getChildPath(name);
- NodeState child = new KernelNodeState(
- kernel, childPath, revision);
- entries.add(new MemoryChildNodeEntry(name, child));
+ entries.add(new KernelChildNodeEntry(name, childPath));
} else if (reader.matches('[')) {
while (reader.read() != ']') {
// skip
@@ -309,4 +322,63 @@ public final class KernelNodeState exten
}
}
+ private Iterable<ChildNodeEntry> iterable(
+ Iterable<Entry<String, String>> set) {
+ return Iterables.transform(
+ set,
+ new Function<Entry<String, String>, ChildNodeEntry>() {
+ @Override
+ public ChildNodeEntry apply(Entry<String, String> input) {
+ return new KernelChildNodeEntry(input);
+ }
+ });
+ }
+
+ private class KernelChildNodeEntry extends AbstractChildNodeEntry {
+
+ private final String name;
+
+ private final String path;
+
+ /**
+ * Creates a child node entry with the given name and referenced
+ * child node state.
+ *
+ * @param name child node name
+ * @param path child node path
+ */
+ public KernelChildNodeEntry(String name, String path) {
+ assert name != null;
+ assert path != null;
+
+ this.name = name;
+ this.path = path;
+ }
+
+ /**
+ * Utility constructor that copies the name and referenced
+ * child node state from the given map entry.
+ *
+ * @param entry map entry
+ */
+ public KernelChildNodeEntry(Map.Entry<String, String> entry) {
+ this(entry.getKey(), entry.getValue());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public NodeState getNodeState() {
+ try {
+ return cache.get(revision + path);
+ } catch (ExecutionException e) {
+ throw new MicroKernelException(e);
+ }
+ }
+
+ }
+
}
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=1381301&r1=1381300&r2=1381301&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 Sep 5 19:08:38 2012
@@ -16,9 +16,12 @@
*/
package org.apache.jackrabbit.oak.kernel;
+import java.util.concurrent.ExecutionException;
+
import javax.annotation.Nonnull;
import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
import org.apache.jackrabbit.oak.api.CoreValueFactory;
import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
import org.apache.jackrabbit.oak.spi.commit.CommitHook;
@@ -30,6 +33,10 @@ import org.apache.jackrabbit.oak.spi.sta
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
/**
* {@code NodeStore} implementations against {@link MicroKernel}.
*/
@@ -52,6 +59,19 @@ public class KernelNodeStore implements
@Nonnull
private volatile Observer observer = EmptyObserver.INSTANCE;
+ private final LoadingCache<String, KernelNodeState> cache =
+ CacheBuilder.newBuilder().maximumSize(100000).build(
+ new CacheLoader<String, KernelNodeState>() {
+ @Override
+ public KernelNodeState load(String key) {
+ int slash = key.indexOf('/');
+ String revision = key.substring(0, slash);
+ String path = key.substring(slash);
+ return new KernelNodeState(
+ kernel, path, revision, cache);
+ }
+ });
+
/**
* State of the current root node.
*/
@@ -60,7 +80,11 @@ public class KernelNodeStore implements
public KernelNodeStore(MicroKernel kernel) {
assert kernel != null;
this.kernel = kernel;
- this.root = new KernelNodeState(kernel, "/", kernel.getHeadRevision());
+ try {
+ this.root = cache.get(kernel.getHeadRevision() + "/");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Nonnull
@@ -86,11 +110,11 @@ public class KernelNodeStore implements
//----------------------------------------------------------< NodeStore >---
@Override
- public synchronized NodeState getRoot() {
+ public synchronized KernelNodeState getRoot() {
String revision = kernel.getHeadRevision();
if (!revision.equals(root.getRevision())) {
NodeState before = root;
- root = new KernelNodeState(kernel, "/", kernel.getHeadRevision());
+ root = getRootState(revision);
observer.contentChanged(this, before, root);
}
return root;
@@ -98,7 +122,7 @@ public class KernelNodeStore implements
@Override
public NodeStoreBranch branch() {
- return new KernelNodeStoreBranch(this);
+ return new KernelNodeStoreBranch(this, getRoot());
}
@Override
@@ -106,7 +130,7 @@ public class KernelNodeStore implements
if (base instanceof KernelNodeState) {
KernelNodeState kbase = (KernelNodeState) base;
if ("/".equals(kbase.getPath())) {
- return new KernelRootBuilder(kernel, kbase.getRevision());
+ return new KernelRootBuilder(kernel, kbase);
}
}
return new MemoryNodeBuilder(base);
@@ -124,4 +148,12 @@ public class KernelNodeStore implements
return kernel;
}
+ KernelNodeState getRootState(String revision) {
+ try {
+ return cache.get(revision + "/");
+ } catch (ExecutionException e) {
+ throw new MicroKernelException(e);
+ }
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Wed Sep 5 19:08:38 2012
@@ -52,11 +52,10 @@ class KernelNodeStoreBranch implements N
/** Last state which was committed to this branch */
private NodeState committed;
- KernelNodeStoreBranch(KernelNodeStore store) {
+ KernelNodeStoreBranch(KernelNodeStore store, KernelNodeState root) {
this.store = store;
- MicroKernel kernel = store.getKernel();
- this.headRevision = kernel.getHeadRevision();
- this.currentRoot = new KernelNodeState(kernel, "/", headRevision);
+ this.headRevision = root.getRevision();
+ this.currentRoot = root;
this.base = currentRoot;
this.committed = currentRoot;
}
@@ -140,7 +139,7 @@ class KernelNodeStoreBranch implements N
String mergedRevision = kernel.merge(branchRevision, null);
branchRevision = null;
currentRoot = null;
- return new KernelNodeState(kernel, "/", mergedRevision);
+ return store.getRootState(mergedRevision);
}
}
catch (MicroKernelException e) {
@@ -171,7 +170,7 @@ class KernelNodeStoreBranch implements N
}
branchRevision = kernel.commit("", jsop, branchRevision, null);
- currentRoot = new KernelNodeState(kernel, "/", branchRevision);
+ currentRoot = store.getRootState(branchRevision);
committed = currentRoot;
}
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java Wed Sep 5 19:08:38 2012
@@ -45,10 +45,10 @@ class KernelRootBuilder extends MemoryNo
private int updates = 0;
- public KernelRootBuilder(MicroKernel kernel, String revision) {
- super(new KernelNodeState(kernel, "/", revision));
+ public KernelRootBuilder(MicroKernel kernel, KernelNodeState state) {
+ super(checkNotNull(state));
this.kernel = checkNotNull(kernel);
- this.baseRevision = checkNotNull(revision);
+ this.baseRevision = state.getRevision();
this.branchRevision = null;
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java Wed Sep 5 19:08:38 2012
@@ -26,7 +26,6 @@ import org.apache.jackrabbit.mk.api.Micr
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.CoreValueFactory;
-import org.apache.jackrabbit.oak.kernel.KernelNodeState;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
import org.apache.jackrabbit.oak.query.index.TraversingIndex;
@@ -44,11 +43,13 @@ public class QueryEngineImpl {
static final String XPATH = "xpath";
static final String JQOM = "JCR-JQOM";
+ private final NodeStore store;
private final MicroKernel mk;
private final CoreValueFactory vf;
private final QueryIndexProvider indexProvider;
public QueryEngineImpl(NodeStore store, MicroKernel mk, QueryIndexProvider indexProvider) {
+ this.store = store;
this.mk = mk;
this.vf = store.getValueFactory();
this.indexProvider = indexProvider;
@@ -108,7 +109,7 @@ public class QueryEngineImpl {
q.setQueryEngine(this);
q.prepare();
String revision = mk.getHeadRevision();
- return q.executeQuery(revision, new KernelNodeState(mk, "/", revision));
+ return q.executeQuery(revision, store.getRoot());
}
public QueryIndex getBestIndex(FilterImpl filter) {
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java Wed Sep 5 19:08:38 2012
@@ -30,16 +30,32 @@ import org.apache.jackrabbit.oak.kernel.
import org.apache.jackrabbit.oak.spi.Cursor;
import org.junit.Test;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
/**
* Tests the TraversingCursor.
*/
public class TraversingCursorTest {
+ private final MicroKernel mk = new MicroKernelImpl();
+
+ private final LoadingCache<String, KernelNodeState> cache =
+ CacheBuilder.newBuilder().build(new CacheLoader<String, KernelNodeState>() {
+ @Override
+ public KernelNodeState load(String key) throws Exception {
+ int slash = key.indexOf('/');
+ String revision = key.substring(0, slash);
+ String path = key.substring(slash);
+ return new KernelNodeState(mk, path, revision, cache);
+ }
+ });
+
@Test
public void traverse() throws Exception {
TraversingIndex t = new TraversingIndex();
- MicroKernel mk = new MicroKernelImpl();
String head = mk.getHeadRevision();
head = mk.commit("/", "+ \"parents\": { \"p0\": {\"id\": \"0\"}, \"p1\": {\"id\": \"1\"}, \"p2\": {\"id\": \"2\"}}", head, "");
head = mk.commit("/", "+ \"children\": { \"c1\": {\"p\": \"1\"}, \"c2\": {\"p\": \"1\"}, \"c3\": {\"p\": \"2\"}, \"c4\": {\"p\": \"3\"}}", head, "");
@@ -47,7 +63,7 @@ public class TraversingCursorTest {
f.setPath("/");
List<String> paths = new ArrayList<String>();
- Cursor c = t.query(f, head, new KernelNodeState(mk, "/", head));
+ Cursor c = t.query(f, head, new KernelNodeState(mk, "/", head, cache));
while (c.next()) {
paths.add(c.currentRow().getPath());
}
@@ -62,7 +78,7 @@ public class TraversingCursorTest {
assertFalse(c.next());
f.setPath("/nowhere");
- c = t.query(f, head, new KernelNodeState(mk, "/", head));
+ c = t.query(f, head, new KernelNodeState(mk, "/", head, cache));
assertFalse(c.next());
// endure it stays false
assertFalse(c.next());
Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java?rev=1381301&r1=1381300&r2=1381301&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java (original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java Wed Sep 5 19:08:38 2012
@@ -235,6 +235,8 @@ public class H2Persistence implements GC
}
}
+ private static final NotFoundException NFE = new NotFoundException();
+
public ChildNodeEntriesMap readCNEMap(Id id) throws NotFoundException, Exception {
Connection con = cp.getConnection();
try {
@@ -246,7 +248,7 @@ public class H2Persistence implements GC
ByteArrayInputStream in = new ByteArrayInputStream(rs.getBytes(1));
return ChildNodeEntriesMap.deserialize(new BinaryBinding(in));
} else {
- throw new NotFoundException(id.toString());
+ throw NFE; // new NotFoundException(id.toString());
}
} finally {
stmt.close();