Author: tomekr
Date: Thu Jul 14 10:33:53 2016
New Revision: 1752612

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

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

Added: 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java?rev=1752612&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java
 (added)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTracker.java
 Thu Jul 14 10:33:53 2016
@@ -0,0 +1,118 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class CacheChangesTracker {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(CacheChangesTracker.class);
+
+    static final int ENTRIES_SCOPED = 1000;
+
+    static final int ENTRIES_OPEN = 10000;
+
+    private final List<CacheChangesTracker> changeTrackers;
+
+    private final Predicate<String> keyFilter;
+
+    private final LazyBloomFilter lazyBloomFilter;
+
+    CacheChangesTracker(Predicate<String> keyFilter, List<CacheChangesTracker> 
changeTrackers, int bloomFilterSize) {
+        this.changeTrackers = changeTrackers;
+        this.keyFilter = keyFilter;
+        this.lazyBloomFilter = new LazyBloomFilter(bloomFilterSize);
+        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);
+
+        if (LOG.isDebugEnabled()) {
+            if (lazyBloomFilter.filter == null) {
+                LOG.debug("Disposing CacheChangesTracker for {}, no filter was 
needed", keyFilter);
+            } else {
+                LOG.debug("Disposing CacheChangesTracker for {}, filter fpp 
was: {}", keyFilter, lazyBloomFilter.filter.expectedFpp());
+            }
+        }
+    }
+
+    public static class LazyBloomFilter {
+
+        private static final double FPP = 0.01d;
+
+        private final int entries;
+
+        private volatile BloomFilter<String> filter;
+
+        public LazyBloomFilter(int entries) {
+            this.entries = entries;
+        }
+
+        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/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java?rev=1752612&r1=1752611&r2=1752612&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java
 Thu Jul 14 10:33:53 2016
@@ -18,16 +18,22 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 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;
@@ -61,6 +67,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 +79,7 @@ public class NodeDocumentCache implement
         this.prevDocumentsCache = prevDocumentsCache;
         this.prevDocumentsCacheStats = prevDocumentsCacheStats;
         this.locks = locks;
+        this.changeTrackers = new CopyOnWriteArrayList<CacheChangesTracker>();
     }
 
     /**
@@ -86,6 +95,10 @@ public class NodeDocumentCache implement
             } else {
                 nodeDocumentsCache.invalidate(new StringValue(key));
             }
+
+            for (CacheChangesTracker tracker : changeTrackers) {
+                tracker.invalidateDocument(key);
+            }
         } finally {
             lock.unlock();
         }
@@ -143,12 +156,26 @@ 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 {
-        if (isLeafPreviousDocId(key)) {
-            return prevDocumentsCache.get(new StringValue(key), valueLoader);
-        } else {
-            return nodeDocumentsCache.get(new StringValue(key), valueLoader);
+        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), 
wrappedLoader);
+            } else {
+                return nodeDocumentsCache.get(new StringValue(key), 
wrappedLoader);
+            }
+        } finally {
+            lock.unlock();
         }
     }
 
@@ -189,23 +216,11 @@ public class NodeDocumentCache implement
         Lock lock = locks.acquire(id);
         try {
             NodeDocument cachedDoc = getIfPresent(id);
-            if (cachedDoc == null || cachedDoc == NodeDocument.NULL) {
+            if (isNewer(cachedDoc, doc)) {
                 newerDoc = doc;
                 putInternal(doc);
             } else {
-                Long cachedModCount = cachedDoc.getModCount();
-                Long modCount = doc.getModCount();
-
-                if (cachedModCount == null || modCount == null) {
-                    throw new IllegalStateException("Missing " + 
Document.MOD_COUNT);
-                }
-
-                if (modCount > cachedModCount) {
-                    newerDoc = doc;
-                    putInternal(doc);
-                } else {
-                    newerDoc = cachedDoc;
-                }
+                newerDoc = cachedDoc;
             }
         } finally {
             lock.unlock();
@@ -321,6 +336,110 @@ public class NodeDocumentCache implement
         }
     }
 
+    /**
+     * Registers a new CacheChangesTracker that records all puts and
+     * invalidations related to children of the given parent.
+     *
+     * @param fromKey only keys larger than this key will be tracked
+     * @param toKey only keys smaller than this key will be tracked
+     * @return new tracker
+     */
+    public CacheChangesTracker registerTracker(final String fromKey, final 
String toKey) {
+        final int bloomFilterSize;
+        if (toKey.equals(NodeDocument.MAX_ID_VALUE)) {
+            bloomFilterSize = CacheChangesTracker.ENTRIES_OPEN;
+        } else {
+            bloomFilterSize = CacheChangesTracker.ENTRIES_SCOPED;
+        }
+        return new CacheChangesTracker(new Predicate<String>() {
+            @Override
+            public boolean apply(@Nullable String input) {
+                return input != null && fromKey.compareTo(input) < 0 && 
toKey.compareTo(input) > 0;
+            }
+
+            @Override
+            public String toString() {
+                return String.format("key range: <%s, %s>", fromKey, toKey);
+            }
+        }, changeTrackers, bloomFilterSize);
+    }
+
+    /**
+     * Registers a new CacheChangesTracker that records all puts and
+     * invalidations related to the given documents
+     *
+     * @param keys these documents will be tracked
+     * @return new tracker
+     */
+    public CacheChangesTracker registerTracker(final Set<String> keys) {
+        return new CacheChangesTracker(new Predicate<String>() {
+            @Override
+            public boolean apply(@Nullable String input) {
+                return input != null && keys.contains(input);
+            }
+
+            @Override
+            public String toString() {
+                StringBuilder builder = new StringBuilder("key set [");
+                Iterator<String> it = keys.iterator();
+                int i = 0;
+                while (it.hasNext() && i++ < 3) {
+                    builder.append(it.next());
+                    if (it.hasNext()) {
+                        builder.append(", ");
+                    }
+                }
+                if (it.hasNext()) {
+                    builder.append("...");
+                }
+                builder.append("]").append(" (").append(keys.size()).append(" 
elements)");
+                return builder.toString();
+            }
+        }, changeTrackers, CacheChangesTracker.ENTRIES_SCOPED);
+    }
+
+    /**
+     * 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 {
+                NodeDocument cachedDoc = getIfPresent(id);
+                // if an old document is present in the cache, we can simply 
update it
+                if (cachedDoc != null && isNewer(cachedDoc, d)) {
+                    putInternal(d);
+                // if the document hasn't been invalidated or added during the 
tracker lifetime,
+                // we can put it as well
+                } else if (cachedDoc == null && 
!tracker.mightBeenAffected(id)) {
+                    putInternal(d);
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
     //----------------------------< internal 
>----------------------------------
 
     /**
@@ -334,5 +453,32 @@ public class NodeDocumentCache implement
         } else {
             nodeDocumentsCache.put(new StringValue(doc.getId()), doc);
         }
+        for (CacheChangesTracker tracker : changeTrackers) {
+            tracker.putDocument(doc.getId());
+        }
+    }
+
+    /**
+     * Check if the doc is more recent than the cachedDoc. If the cachedDoc
+     * is {@code null} or {@code NodeDocument.NULL}, the doc will be considered
+     * as more recent as well.
+     *
+     * @param cachedDoc the document already present in cache
+     * @param doc the tested document
+     * @return {@code true} iff the cacheDoc is null or older than the doc
+     */
+    private boolean isNewer(@Nullable NodeDocument cachedDoc, @Nonnull 
NodeDocument doc) {
+        if (cachedDoc == null || cachedDoc == NodeDocument.NULL) {
+            return true;
+        }
+
+        Long cachedModCount = cachedDoc.getModCount();
+        Long modCount = doc.getModCount();
+
+        if (cachedModCount == null || modCount == null) {
+            throw new IllegalStateException("Missing " + Document.MOD_COUNT);
+        }
+
+        return modCount > cachedModCount;
     }
 }

Modified: 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java?rev=1752612&r1=1752611&r2=1752612&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
 Thu Jul 14 10:33:53 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,10 +67,12 @@ 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.ModificationStamp;
 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;
@@ -137,7 +138,7 @@ public class MongoDocumentStore implemen
 
     private final NodeDocumentCache nodesCache;
 
-    private final TreeNodeDocumentLocks nodeLocks;
+    private final NodeDocumentLocks nodeLocks;
 
     private Clock clock = Clock.SIMPLE;
 
@@ -172,17 +173,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.
@@ -242,7 +232,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 {}, " +
@@ -525,27 +515,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,
@@ -553,8 +527,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);
@@ -586,11 +559,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(fromKey, toKey);
+        }
         try {
-            lockTime = withLock ? watch.elapsed(TimeUnit.MILLISECONDS) : -1;
             DBCursor cursor = dbCollection.find(query).sort(BY_ID_ASC);
             if (!disableIndexHint && !hasModifiedIdCompoundIndex) {
                 cursor.hint(hint);
@@ -615,21 +591,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);
@@ -1537,18 +1513,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/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java?rev=1752612&view=auto
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java
 (added)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java
 Thu Jul 14 10:33:53 2016
@@ -0,0 +1,139 @@
+/*
+ * 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.apache.jackrabbit.oak.plugins.document.util.Utils;
+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.apache.jackrabbit.oak.plugins.document.util.Utils.getKeyLowerLimit;
+import static 
org.apache.jackrabbit.oak.plugins.document.util.Utils.getKeyUpperLimit;
+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, 100);
+
+        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(getKeyLowerLimit("/parent"), getKeyUpperLimit("/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(getKeyLowerLimit("/parent"), getKeyUpperLimit("/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/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java?rev=1752612&r1=1752611&r2=1752612&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
 Thu Jul 14 10:33:53 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/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java?rev=1752612&r1=1752611&r2=1752612&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreTest.java
 Thu Jul 14 10:33:53 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);
-        }
     }
 }



Reply via email to