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 mr...@apache.org on 2013/03/04 15:55:37 UTC

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

Author: mreutegg
Date: Mon Mar  4 14:55:36 2013
New Revision: 1452336

URL: http://svn.apache.org/r1452336
Log:
OAK-665: Excessive MK.nodeExists() calls with many child nodes

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.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=1452336&r1=1452335&r2=1452336&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 Mon Mar  4 14:55:36 2013
@@ -18,6 +18,9 @@
  */
 package org.apache.jackrabbit.oak.kernel;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -31,6 +34,8 @@ import javax.annotation.Nonnull;
 import javax.jcr.PropertyType;
 
 import com.google.common.base.Function;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -67,6 +72,33 @@ public final class KernelNodeState exten
      */
     static final int MAX_CHILD_NODE_NAMES = 1000;
 
+    /**
+     * Dummy cache instance for static {@link #NULL} kernel node state.
+     */
+    private static final LoadingCache<String, KernelNodeState> DUMMY_CACHE =
+        CacheBuilder.newBuilder().build(new CacheLoader<String, KernelNodeState>() {
+            @Override
+            public KernelNodeState load(String key) throws Exception {
+                throw new UnsupportedOperationException();
+            }
+        });
+
+    /**
+     * This <code>NULL</code> kernel node state is used as a value in the
+     * {@link #cache} to indicate that there is no node state at the given
+     * path and revision. This object is only used internally and never leaves
+     * this {@link KernelNodeState}.
+     */
+    private static final KernelNodeState NULL = new KernelNodeState(
+            (MicroKernel) Proxy.newProxyInstance(MicroKernel.class.getClassLoader(),
+                    new Class[]{MicroKernel.class}, new InvocationHandler() {
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+            throw new UnsupportedOperationException();
+        }
+    }), "null", "null", DUMMY_CACHE);
+
     private final MicroKernel kernel;
 
     private final String path;
@@ -221,9 +253,18 @@ public final class KernelNodeState exten
             // OAK-506: Avoid the nodeExists() call when already cached
             NodeState state = cache.getIfPresent(revision + path);
             if (state != null) {
-                return state;
-            } else if (kernel.nodeExists(path, revision)) {
+                if (state != NULL) {
+                    return state;
+                } else {
+                    return null;
+                }
+            }
+            // not able to tell from cache if node exists
+            // need to ask MicroKernel
+            if (kernel.nodeExists(path, revision)) {
                 childPath = path;
+            } else {
+                cache.put(revision + path, NULL);
             }
         }
         if (childPath == null) {