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 to...@apache.org on 2016/04/28 12:02:41 UTC

svn commit: r1741403 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/document/cache/ main/java/org/apache/jackrabbit/oak/plugins/document/locks/ main/java/org/apache/jackrabbit/oak/plugins/document/mongo/ test/java/...

Author: tomekr
Date: Thu Apr 28 10:02:41 2016
New Revision: 1741403

URL: http://svn.apache.org/viewvc?rev=1741403&view=rev
Log:
OAK-4112: Replace the query exclusive lock with a cache tracker

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkReadWriteLock.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentsLocksTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java?rev=1741403&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java Thu Apr 28 10:02:41 2016
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document.cache;
+
+import com.google.common.base.Predicate;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.PrimitiveSink;
+
+import java.util.List;
+
+public class CacheChangesTracker {
+
+    private final List<CacheChangesTracker> changeTrackers;
+
+    private final Predicate<String> keyFilter;
+
+    private final LazyBloomFilter lazyBloomFilter;
+
+    CacheChangesTracker(Predicate<String> keyFilter, List<CacheChangesTracker> changeTrackers) {
+        this.changeTrackers = changeTrackers;
+        this.keyFilter = keyFilter;
+        this.lazyBloomFilter = new LazyBloomFilter();
+        changeTrackers.add(this);
+    }
+
+    public void putDocument(String key) {
+        if (keyFilter.apply(key)) {
+            lazyBloomFilter.put(key);
+        }
+    }
+
+    public void invalidateDocument(String key) {
+        if (keyFilter.apply(key)) {
+            lazyBloomFilter.put(key);
+        }
+    }
+
+    public boolean mightBeenAffected(String key) {
+        return keyFilter.apply(key) && lazyBloomFilter.mightContain(key);
+    }
+
+    public void close() {
+        changeTrackers.remove(this);
+    }
+
+    public static class LazyBloomFilter {
+
+        private static final double FPP = 0.01d;
+
+        private static final int ENTRIES = 1000;
+
+        private volatile BloomFilter<String> filter;
+
+        public synchronized void put(String entry) {
+            getFilter().put(entry);
+        }
+
+        public boolean mightContain(String entry) {
+            if (filter == null) {
+                return false;
+            } else {
+                synchronized (this) {
+                    return filter.mightContain(entry);
+                }
+            }
+        }
+
+        private BloomFilter<String> getFilter() {
+            if (filter == null) {
+                filter = BloomFilter.create(new Funnel<String>() {
+                    private static final long serialVersionUID = -7114267990225941161L;
+
+                    @Override
+                    public void funnel(String from, PrimitiveSink into) {
+                        into.putUnencodedChars(from);
+                    }
+                }, ENTRIES, FPP);
+            }
+            return filter;
+        }
+
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java?rev=1741403&r1=1741402&r2=1741403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java Thu Apr 28 10:02:41 2016
@@ -18,16 +18,20 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.locks.Lock;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.apache.jackrabbit.oak.cache.CacheStats;
@@ -36,6 +40,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
 import org.apache.jackrabbit.oak.plugins.document.locks.NodeDocumentLocks;
 import org.apache.jackrabbit.oak.plugins.document.util.StringValue;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 
 import com.google.common.base.Objects;
 import com.google.common.cache.Cache;
@@ -61,6 +66,8 @@ public class NodeDocumentCache implement
 
     private final NodeDocumentLocks locks;
 
+    private final List<CacheChangesTracker> changeTrackers;
+
     public NodeDocumentCache(@Nonnull Cache<CacheValue, NodeDocument> nodeDocumentsCache,
                              @Nonnull CacheStats nodeDocumentsCacheStats,
                              @Nonnull Cache<StringValue, NodeDocument> prevDocumentsCache,
@@ -71,6 +78,7 @@ public class NodeDocumentCache implement
         this.prevDocumentsCache = prevDocumentsCache;
         this.prevDocumentsCacheStats = prevDocumentsCacheStats;
         this.locks = locks;
+        this.changeTrackers = new CopyOnWriteArrayList<CacheChangesTracker>();
     }
 
     /**
@@ -86,6 +94,10 @@ public class NodeDocumentCache implement
             } else {
                 nodeDocumentsCache.invalidate(new StringValue(key));
             }
+
+            for (CacheChangesTracker tracker : changeTrackers) {
+                tracker.invalidateDocument(key);
+            }
         } finally {
             lock.unlock();
         }
@@ -145,14 +157,23 @@ public class NodeDocumentCache implement
      * @return document matching given key
      */
     @Nonnull
-    public NodeDocument get(@Nonnull String key, @Nonnull Callable<NodeDocument> valueLoader)
+    public NodeDocument get(@Nonnull final String key, @Nonnull final Callable<NodeDocument> valueLoader)
             throws ExecutionException {
+        Callable<NodeDocument> wrappedLoader = new Callable<NodeDocument>() {
+            @Override
+            public NodeDocument call() throws Exception {
+                for (CacheChangesTracker tracker : changeTrackers) {
+                    tracker.putDocument(key);
+                }
+                return valueLoader.call();
+            }
+        };
         Lock lock = locks.acquire(key);
         try {
             if (isLeafPreviousDocId(key)) {
-                return prevDocumentsCache.get(new StringValue(key), valueLoader);
+                return prevDocumentsCache.get(new StringValue(key), wrappedLoader);
             } else {
-                return nodeDocumentsCache.get(new StringValue(key), valueLoader);
+                return nodeDocumentsCache.get(new StringValue(key), wrappedLoader);
             }
         } finally {
             lock.unlock();
@@ -328,6 +349,63 @@ public class NodeDocumentCache implement
         }
     }
 
+    /**
+     * Registers a new CacheChangesTracker that records all puts and
+     * invalidations related to children of the given parent.
+     *
+     * @param parentId children of this parent will be tracked
+     * @return new tracker
+     */
+    public CacheChangesTracker registerTracker(final String parentId) {
+        return new CacheChangesTracker(new Predicate<String>() {
+            @Override
+            public boolean apply(@Nullable String input) {
+                return input != null && parentId.equals(Utils.getParentId(input));
+            }
+        }, changeTrackers);
+    }
+
+   /**
+     * Updates the cache with all the documents that:
+     *
+     * (1) currently have their older versions in the cache or
+     * (2) have been neither put nor invalidated during the tracker lifetime.
+     *
+     * We can't cache documents that has been invalidated during the tracker
+     * lifetime, as it's possible that the invalidated version was newer than
+     * the one passed in the docs parameter.
+     *
+     * If the document has been added during the tracker lifetime, but it is not
+     * present in the cache anymore, it means it may have been evicted, so we
+     * can't re-add it for the same reason as above.
+     *
+     * @param tracker
+     *            used to decide whether the docs should be put into cache
+     * @param docs
+     *            to put into cache
+     */
+    public void putNonConflictingDocs(CacheChangesTracker tracker, List<NodeDocument> docs) {
+        for (NodeDocument d : docs) {
+            if (d == null || d == NodeDocument.NULL) {
+                continue;
+            }
+            String id = d.getId();
+            Lock lock = locks.acquire(id);
+            try {
+                // if an old document is present in the cache, we can simply update it
+                if (getIfPresent(id) != null) {
+                    putIfNewer(d);
+                // if the document hasn't been invalidated or added during the tracker lifetime,
+                // we can put it as well
+                } else if (!tracker.mightBeenAffected(id)) {
+                    putIfNewer(d);
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
     //----------------------------< internal >----------------------------------
 
     /**
@@ -341,5 +419,8 @@ public class NodeDocumentCache implement
         } else {
             nodeDocumentsCache.put(new StringValue(doc.getId()), doc);
         }
+        for (CacheChangesTracker tracker : changeTrackers) {
+            tracker.putDocument(doc.getId());
+        }
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java?rev=1741403&r1=1741402&r2=1741403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java Thu Apr 28 10:02:41 2016
@@ -47,7 +47,6 @@ import com.google.common.collect.Iterato
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 import com.mongodb.MongoClientURI;
-import com.mongodb.MongoExecutionTimeoutException;
 import com.mongodb.QueryOperators;
 import com.mongodb.ReadPreference;
 
@@ -68,9 +67,11 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
 import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
 import org.apache.jackrabbit.oak.plugins.document.UpdateUtils;
+import org.apache.jackrabbit.oak.plugins.document.cache.CacheChangesTracker;
 import org.apache.jackrabbit.oak.plugins.document.cache.CacheInvalidationStats;
 import org.apache.jackrabbit.oak.plugins.document.cache.NodeDocumentCache;
-import org.apache.jackrabbit.oak.plugins.document.locks.TreeNodeDocumentLocks;
+import org.apache.jackrabbit.oak.plugins.document.locks.NodeDocumentLocks;
+import org.apache.jackrabbit.oak.plugins.document.locks.StripedNodeDocumentLocks;
 import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.apache.jackrabbit.oak.util.PerfLogger;
@@ -135,7 +136,7 @@ public class MongoDocumentStore implemen
 
     private final NodeDocumentCache nodesCache;
 
-    private final TreeNodeDocumentLocks nodeLocks;
+    private final NodeDocumentLocks nodeLocks;
 
     private Clock clock = Clock.SIMPLE;
 
@@ -170,17 +171,6 @@ public class MongoDocumentStore implemen
             Long.getLong("oak.mongo.maxQueryTimeMS", TimeUnit.MINUTES.toMillis(1));
 
     /**
-     * Duration in milliseconds after a mongo query with an additional
-     * constraint (e.g. _modified) on the NODES collection times out and is
-     * executed again without holding a {@link TreeNodeDocumentLocks.TreeLock}
-     * and without updating the cache with data retrieved from MongoDB.
-     * <p>
-     * Default is 3000 (three seconds).
-     */
-    private long maxLockedQueryTimeMS =
-            Long.getLong("oak.mongo.maxLockedQueryTimeMS", TimeUnit.SECONDS.toMillis(3));
-
-    /**
      * The number of documents to put into one bulk update.
      * <p>
      * Default is 30.
@@ -240,7 +230,7 @@ public class MongoDocumentStore implemen
         // index on _modified for journal entries
         createIndex(journal, JournalEntry.MODIFIED, true, false, false);
 
-        this.nodeLocks = new TreeNodeDocumentLocks();
+        this.nodeLocks = new StripedNodeDocumentLocks();
         this.nodesCache = builder.buildNodeDocumentCache(this, nodeLocks);
 
         LOG.info("Configuration maxReplicationLagMillis {}, " +
@@ -523,27 +513,11 @@ public class MongoDocumentStore implemen
                                               String indexedProperty,
                                               long startValue,
                                               int limit) {
-        boolean withLock = true;
-        if (collection == Collection.NODES && indexedProperty != null) {
-            long maxQueryTime;
-            if (maxQueryTimeMS > 0) {
-                maxQueryTime = Math.min(maxQueryTimeMS, maxLockedQueryTimeMS);
-            } else {
-                maxQueryTime = maxLockedQueryTimeMS;
-            }
-            try {
-                return queryInternal(collection, fromKey, toKey, indexedProperty,
-                        startValue, limit, maxQueryTime, true);
-            } catch (MongoExecutionTimeoutException e) {
-                LOG.info("query timed out after {} milliseconds and will be retried without lock {}",
-                        maxQueryTime, Lists.newArrayList(fromKey, toKey, indexedProperty, startValue, limit));
-                withLock = false;
-            }
-        }
         return queryInternal(collection, fromKey, toKey, indexedProperty,
-                startValue, limit, maxQueryTimeMS, withLock);
+                startValue, limit, maxQueryTimeMS);
     }
 
+    @SuppressWarnings("unchecked")
     @Nonnull
     <T extends Document> List<T> queryInternal(Collection<T> collection,
                                                        String fromKey,
@@ -551,8 +525,7 @@ public class MongoDocumentStore implemen
                                                        String indexedProperty,
                                                        long startValue,
                                                        int limit,
-                                                       long maxQueryTime,
-                                                       boolean withLock) {
+                                                       long maxQueryTime) {
         log("query", fromKey, toKey, indexedProperty, startValue, limit);
         DBCollection dbCollection = getDBCollection(collection);
         QueryBuilder queryBuilder = QueryBuilder.start(Document.ID);
@@ -584,11 +557,14 @@ public class MongoDocumentStore implemen
         String parentId = Utils.getParentIdFromLowerLimit(fromKey);
         long lockTime = -1;
         final Stopwatch watch  = startWatch();
-        Lock lock = withLock ? nodeLocks.acquireExclusive(parentId != null ? parentId : "") : null;
+
         boolean isSlaveOk = false;
         int resultSize = 0;
+        CacheChangesTracker cacheChangesTracker = null;
+        if (parentId != null && collection == Collection.NODES) {
+            cacheChangesTracker = nodesCache.registerTracker(parentId);
+        }
         try {
-            lockTime = withLock ? watch.elapsed(TimeUnit.MILLISECONDS) : -1;
             DBCursor cursor = dbCollection.find(query).sort(BY_ID_ASC);
             if (!disableIndexHint && !hasModifiedIdCompoundIndex) {
                 cursor.hint(hint);
@@ -613,21 +589,21 @@ public class MongoDocumentStore implemen
                 for (int i = 0; i < limit && cursor.hasNext(); i++) {
                     DBObject o = cursor.next();
                     T doc = convertFromDBObject(collection, o);
-                    if (collection == Collection.NODES
-                            && doc != null
-                            && lock != null) {
-                        nodesCache.putIfNewer((NodeDocument) doc);
-                    }
                     list.add(doc);
                 }
                 resultSize = list.size();
             } finally {
                 cursor.close();
             }
+
+            if (cacheChangesTracker != null) {
+                nodesCache.putNonConflictingDocs(cacheChangesTracker, (List<NodeDocument>) list);
+            }
+
             return list;
         } finally {
-            if (lock != null) {
-                lock.unlock();
+            if (cacheChangesTracker != null) {
+                cacheChangesTracker.close();
             }
             stats.doneQuery(watch.elapsed(TimeUnit.NANOSECONDS), collection, fromKey, toKey,
                     indexedProperty != null , resultSize, lockTime, isSlaveOk);
@@ -1528,18 +1504,6 @@ public class MongoDocumentStore implemen
         this.clock = clock;
     }
 
-    void setMaxLockedQueryTimeMS(long maxLockedQueryTimeMS) {
-        this.maxLockedQueryTimeMS = maxLockedQueryTimeMS;
-    }
-
-    void resetLockAcquisitionCount() {
-        nodeLocks.resetLockAcquisitionCount();
-    }
-
-    long getLockAcquisitionCount() {
-        return nodeLocks.getLockAcquisitionCount();
-    }
-
     NodeDocumentCache getNodeDocumentCache() {
         return nodesCache;
     }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java?rev=1741403&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java Thu Apr 28 10:02:41 2016
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document.cache;
+
+import com.google.common.base.Predicate;
+import com.google.common.cache.Cache;
+import org.apache.jackrabbit.oak.cache.CacheLIRS;
+import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.cache.CacheValue;
+import org.apache.jackrabbit.oak.plugins.document.Collection;
+import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
+import org.apache.jackrabbit.oak.plugins.document.locks.NodeDocumentLocks;
+import org.apache.jackrabbit.oak.plugins.document.locks.StripedNodeDocumentLocks;
+import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.util.StringValue;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class CacheChangesTrackerTest {
+
+    private DocumentStore ds;
+
+    @Before
+    public void createDS() {
+        ds = new MemoryDocumentStore();
+    }
+
+    @Test
+    public void testTracker() {
+        List<CacheChangesTracker> list = new ArrayList<CacheChangesTracker>();
+        CacheChangesTracker tracker = new CacheChangesTracker(new Predicate<String>() {
+            @Override
+            public boolean apply(@Nullable String input) {
+                return !"ignored".equals(input);
+            }
+        }, list);
+
+        assertFalse(tracker.mightBeenAffected("xyz"));
+        assertFalse(tracker.mightBeenAffected("abc"));
+
+        tracker.putDocument("xyz");
+        assertTrue(tracker.mightBeenAffected("xyz"));
+
+        tracker.invalidateDocument("abc");
+        assertTrue(tracker.mightBeenAffected("abc"));
+
+        tracker.putDocument("ignored");
+        tracker.invalidateDocument("ignored");
+        assertFalse(tracker.mightBeenAffected("ignored"));
+
+        tracker.close();
+        assertTrue(list.isEmpty());
+    }
+
+    @Test
+    public void testRegisterChildrenTracker() {
+        NodeDocumentCache cache = createCache();
+        CacheChangesTracker tracker = cache.registerTracker("1:/parent");
+
+        assertFalse(tracker.mightBeenAffected("2:/parent/xyz"));
+        assertFalse(tracker.mightBeenAffected("2:/parent/abc"));
+
+        cache.put(createDoc("2:/parent/xyz"));
+        assertTrue(tracker.mightBeenAffected("2:/parent/xyz"));
+
+        cache.invalidate("2:/parent/abc");
+        assertTrue(tracker.mightBeenAffected("2:/parent/abc"));
+
+        cache.invalidate("2:/other-parent/abc");
+        assertFalse(tracker.mightBeenAffected("2:/other-parent/abc"));
+
+        tracker.close();
+
+        cache.invalidate("2:/parent/123");
+        assertFalse(tracker.mightBeenAffected("2:/parent/123"));
+    }
+
+    @Test
+    public void testGetLoaderAffectsTracker() throws ExecutionException {
+        NodeDocumentCache cache = createCache();
+        CacheChangesTracker tracker = cache.registerTracker("1:/parent");
+
+        assertFalse(tracker.mightBeenAffected("2:/parent/xyz"));
+
+        cache.getIfPresent("2:/parent/xyz");
+        assertFalse(tracker.mightBeenAffected("2:/parent/xyz"));
+
+        cache.get("2:/parent/xyz", new Callable<NodeDocument>() {
+            @Override
+            public NodeDocument call() throws Exception {
+                return createDoc("2:/parent/xyz");
+            }
+        });
+        assertTrue(tracker.mightBeenAffected("2:/parent/xyz"));
+    }
+
+    private NodeDocumentCache createCache() {
+        Cache<CacheValue, NodeDocument> nodeDocumentsCache = new CacheLIRS<CacheValue, NodeDocument>(10);
+        Cache<StringValue, NodeDocument> prevDocumentsCache = new CacheLIRS<StringValue, NodeDocument>(10);
+        CacheStats nodeDocumentsCacheStats = Mockito.mock(CacheStats.class);
+        CacheStats prevDocumentsCacheStats = Mockito.mock(CacheStats.class);
+        NodeDocumentLocks locks = new StripedNodeDocumentLocks();
+        return new NodeDocumentCache(nodeDocumentsCache, nodeDocumentsCacheStats, prevDocumentsCache, prevDocumentsCacheStats, locks);
+    }
+
+    private NodeDocument createDoc(String id) {
+        NodeDocument doc = Collection.NODES.newDocument(ds);
+        doc.put("_id", id);
+        return doc;
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java?rev=1741403&r1=1741402&r2=1741403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java Thu Apr 28 10:02:41 2016
@@ -19,30 +19,14 @@ package org.apache.jackrabbit.oak.plugin
 import static org.junit.Assert.fail;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.locks.Lock;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
-@RunWith(Parameterized.class)
 public class NodeDocumentLocksTest {
 
-    @Parameters(name = "{1}")
-    public static Collection<Object[]> data() {
-        return Arrays.asList(new Object[][] { { new StripedNodeDocumentLocks(), "StripedNodeDocumentLocks" },
-                { new TreeNodeDocumentLocks(), "TreeNodeDocumentLocks" } });
-    }
-
-    private final NodeDocumentLocks locks;
-
-    public NodeDocumentLocksTest(NodeDocumentLocks locks, String name) {
-        this.locks = locks;
-    }
+    private final NodeDocumentLocks locks = new StripedNodeDocumentLocks();
 
     @Test
     public void testBulkAcquireNonConflicting() throws InterruptedException {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java?rev=1741403&r1=1741402&r2=1741403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java Thu Apr 28 10:02:41 2016
@@ -16,11 +16,6 @@
  */
 package org.apache.jackrabbit.oak.plugins.document.mongo;
 
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.annotation.Nonnull;
-
 import com.mongodb.DB;
 
 import org.apache.jackrabbit.oak.plugins.document.AbstractMongoConnectionTest;
@@ -30,14 +25,11 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.document.JournalEntry;
 import org.apache.jackrabbit.oak.plugins.document.MongoUtils;
 import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
-import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 import org.junit.Test;
 
 import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoUtils.hasIndex;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 /**
  * <code>MongoDocumentStoreTest</code>...
@@ -57,32 +49,6 @@ public class MongoDocumentStoreTest exte
     }
 
     @Test
-    public void timeoutQuery() {
-        String fromId = Utils.getKeyLowerLimit("/");
-        String toId = Utils.getKeyUpperLimit("/");
-        store.setMaxLockedQueryTimeMS(1);
-        long index = 0;
-        for (int i = 0; i < 100; i++) {
-            // keep adding nodes until the query runs into the timeout
-            StringBuilder sb = new StringBuilder();
-            for (int j = 0; j < 1000; j++) {
-                sb.append("+\"node-").append(index++).append("\":{}");
-            }
-            mk.commit("/", sb.toString(), null, null);
-            store.queriesWithoutLock.set(0);
-            store.resetLockAcquisitionCount();
-            List<NodeDocument> docs = store.query(Collection.NODES, fromId, toId,
-                    "foo", System.currentTimeMillis(), Integer.MAX_VALUE);
-            assertTrue(docs.isEmpty());
-            if (store.queriesWithoutLock.get() > 0) {
-                assertEquals(1, store.getLockAcquisitionCount());
-                return;
-            }
-        }
-        fail("No query timeout triggered even after adding " + index + " nodes");
-    }
-
-    @Test
     public void defaultIndexes() {
         assertTrue(hasIndex(store.getDBCollection(Collection.NODES), Document.ID));
         assertTrue(hasIndex(store.getDBCollection(Collection.NODES), NodeDocument.SD_TYPE));
@@ -94,28 +60,8 @@ public class MongoDocumentStoreTest exte
     }
 
     static final class TestStore extends MongoDocumentStore {
-
-        AtomicInteger queriesWithoutLock = new AtomicInteger();
-
         TestStore(DB db, DocumentMK.Builder builder) {
             super(db, builder);
         }
-
-        @Nonnull
-        @Override
-        <T extends Document> List<T> queryInternal(Collection<T> collection,
-                                                   String fromKey,
-                                                   String toKey,
-                                                   String indexedProperty,
-                                                   long startValue,
-                                                   int limit,
-                                                   long maxQueryTime,
-                                                   boolean withLock) {
-            if (collection == Collection.NODES && !withLock) {
-                queriesWithoutLock.incrementAndGet();
-            }
-            return super.queryInternal(collection, fromKey, toKey,
-                    indexedProperty, startValue, limit, maxQueryTime, withLock);
-        }
     }
 }