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();