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 th...@apache.org on 2017/08/11 16:45:36 UTC

svn commit: r1804821 [1/2] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/ oak-core/...

Author: thomasm
Date: Fri Aug 11 16:45:35 2017
New Revision: 1804821

URL: http://svn.apache.org/viewvc?rev=1804821&view=rev
Log:
OAK-6526 Slow queries: ability to track them via JMX

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsData.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBean.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBeanImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsReporter.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryLimits.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/QueryStatsTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Cursors.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryEngineSettings.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/LargeQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryCostOverheadTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/XPathTest.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java Fri Aug 11 16:45:35 2017
@@ -79,6 +79,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindex;
 import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindexMBean;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsMBean;
 import org.apache.jackrabbit.oak.spi.commit.CompositeConflictHandler;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
@@ -693,6 +694,9 @@ public class Oak {
         regs.add(registerMBean(whiteboard, QueryEngineSettingsMBean.class,
                 queryEngineSettings, QueryEngineSettingsMBean.TYPE, "settings"));
 
+        regs.add(registerMBean(whiteboard, QueryStatsMBean.class,
+                queryEngineSettings.getQueryStats(), QueryStatsMBean.TYPE, "Oak Query Statistics (Extended)"));
+
         // FIXME: OAK-810 move to proper workspace initialization
         // initialize default workspace
         Iterable<WorkspaceInitializer> workspaceInitializers =
@@ -881,16 +885,8 @@ public class Oak {
             settings.setFastQuerySize(fastQuerySize);
         }
 
-        public void setFullTextComparisonWithoutIndex(boolean fullTextComparisonWithoutIndex) {
-            settings.setFullTextComparisonWithoutIndex(fullTextComparisonWithoutIndex);
-        }
-
-        public boolean getFullTextComparisonWithoutIndex() {
-            return settings.getFullTextComparisonWithoutIndex();
-        }
-
-        public boolean isSql2Optimisation() {
-            return settings.isSql2Optimisation();
+        public QueryStatsMBean getQueryStats() {
+            return settings.getQueryStats();
         }
 
         public QueryEngineSettings unwrap() {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Cursors.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Cursors.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Cursors.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Cursors.java Fri Aug 11 16:45:35 2017
@@ -34,7 +34,7 @@ import org.apache.jackrabbit.oak.spi.que
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
@@ -59,11 +59,11 @@ public class Cursors {
     private Cursors() {
     }
     
-    public static Cursor newIntersectionCursor(Cursor a, Cursor b, QueryEngineSettings settings) {
+    public static Cursor newIntersectionCursor(Cursor a, Cursor b, QueryLimits settings) {
         return new IntersectionCursor(a, b, settings);
     }
 
-    public static Cursor newConcatCursor(List<Cursor> cursors, QueryEngineSettings settings) {
+    public static Cursor newConcatCursor(List<Cursor> cursors, QueryLimits settings) {
         return new ConcatCursor(cursors, settings);
     }
 
@@ -73,7 +73,7 @@ public class Cursors {
      * @param paths the paths to iterate over (must return distinct paths)
      * @return the Cursor.
      */
-    public static Cursor newPathCursor(Iterable<String> paths, QueryEngineSettings settings) {
+    public static Cursor newPathCursor(Iterable<String> paths, QueryLimits settings) {
         return new PathCursor(paths.iterator(), true, settings);
     }
 
@@ -84,7 +84,7 @@ public class Cursors {
      * @param paths the paths to iterate over (might contain duplicate entries)
      * @return the Cursor.
      */
-    public static Cursor newPathCursorDistinct(Iterable<String> paths, QueryEngineSettings settings) {
+    public static Cursor newPathCursorDistinct(Iterable<String> paths, QueryLimits settings) {
         return new PathCursor(paths.iterator(), true, settings);
     }
 
@@ -112,7 +112,7 @@ public class Cursors {
      * @param level the ancestor level. Must be {@code >= 1}.
      * @return cursor over the ancestors of <code>c</code> at <code>level</code>.
      */
-    public static Cursor newAncestorCursor(Cursor c, int level, QueryEngineSettings settings) {
+    public static Cursor newAncestorCursor(Cursor c, int level, QueryLimits settings) {
         checkNotNull(c);
         checkArgument(level >= 1);
         return new AncestorCursor(c, level, settings);
@@ -141,7 +141,7 @@ public class Cursors {
      */
     private static class AncestorCursor extends PathCursor {
 
-        public AncestorCursor(Cursor cursor, int level, QueryEngineSettings settings) {
+        public AncestorCursor(Cursor cursor, int level, QueryLimits settings) {
             super(transform(cursor, level), true, settings);
         }
 
@@ -177,7 +177,7 @@ public class Cursors {
 
         private final Iterator<String> iterator;
 
-        public PathCursor(Iterator<String> paths, boolean distinct, final QueryEngineSettings settings) {
+        public PathCursor(Iterator<String> paths, boolean distinct, final QueryLimits settings) {
             Iterator<String> it = paths;
             if (distinct) {
                 it = Iterators.filter(it, new Predicate<String>() {
@@ -232,11 +232,11 @@ public class Cursors {
         
         private boolean closed;
         
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
         
         public TraversingCursor(Filter filter, NodeState rootState) {
             this.filter = filter;
-            this.settings = filter.getQueryEngineSettings();
+            this.settings = filter.getQueryLimits();
 
             String path = filter.getPath();
             parentPath = null;
@@ -362,12 +362,12 @@ public class Cursors {
         private final HashMap<String, IndexRow> secondSet = new HashMap<String, IndexRow>();
         private final HashSet<String> seen = new HashSet<String>();
         private final Cursor first, second;
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
         private boolean init;
         private boolean closed;
         private IndexRow current;
         
-        IntersectionCursor(Cursor first, Cursor second, QueryEngineSettings settings) {
+        IntersectionCursor(Cursor first, Cursor second, QueryLimits settings) {
             this.first = first;
             this.second = second;
             this.settings = settings;
@@ -455,7 +455,7 @@ public class Cursors {
 
         private final HashSet<String> seen = new HashSet<String>();
         private final List<Cursor> cursors;
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
         private boolean init;
         private boolean closed;
 
@@ -463,7 +463,7 @@ public class Cursors {
         private int cursorListIndex;
         private IndexRow current;
 
-        ConcatCursor(List<Cursor> cursors, QueryEngineSettings settings) {
+        ConcatCursor(List<Cursor> cursors, QueryLimits settings) {
             this.cursors = cursors;
             this.settings = settings;
             nextCursor();

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java Fri Aug 11 16:45:35 2017
@@ -198,7 +198,7 @@ public class AggregateIndex implements A
                     Cursor newC = flatten(input, plan, filter, state,
                             path + " and(" + index + ")");
                     c = Cursors.newIntersectionCursor(c, newC,
-                            filter.getQueryEngineSettings());
+                            filter.getQueryLimits());
                 }
                 result.set(c);
                 return true;
@@ -216,7 +216,7 @@ public class AggregateIndex implements A
                             }
                         });
                 result.set(Cursors.newConcatCursor(cursors,
-                        filter.getQueryEngineSettings()));
+                        filter.getQueryLimits()));
                 return true;
             }
         });

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java Fri Aug 11 16:45:35 2017
@@ -41,7 +41,7 @@ public abstract class DiffIndex implemen
 
     @Override
     public Cursor query(Filter filter, NodeState rootState) {
-        return Cursors.newPathCursor(collector.getResults(filter), filter.getQueryEngineSettings());
+        return Cursors.newPathCursor(collector.getResults(filter), filter.getQueryLimits());
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java Fri Aug 11 16:45:35 2017
@@ -94,7 +94,7 @@ class NodeTypeIndex implements QueryInde
             throw new IllegalStateException(
                     "NodeType index is used even when no index is available for filter " + filter);
         }
-        return Cursors.newPathCursorDistinct(lookup.query(filter), filter.getQueryEngineSettings());
+        return Cursors.newPathCursorDistinct(lookup.query(filter), filter.getQueryLimits());
     }
     
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java Fri Aug 11 16:45:35 2017
@@ -41,7 +41,7 @@ import org.apache.jackrabbit.oak.spi.mou
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import com.google.common.collect.Iterables;
@@ -266,7 +266,7 @@ public class PropertyIndexPlan {
     }
 
     Cursor execute() {
-        QueryEngineSettings settings = filter.getQueryEngineSettings();
+        QueryLimits settings = filter.getQueryLimits();
         List<Iterable<String>> iterables = Lists.newArrayList();
         for (IndexStoreStrategy s : strategies) {
             iterables.add(s.query(filter, name, definition, values));

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java Fri Aug 11 16:45:35 2017
@@ -17,8 +17,8 @@
 package org.apache.jackrabbit.oak.plugins.index.property.strategy;
 
 import static com.google.common.collect.Queues.newArrayDeque;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.KEY_COUNT_PROPERTY_NAME;
 
 import java.util.Deque;
@@ -28,24 +28,24 @@ import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import com.google.common.base.Supplier;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.counter.ApproximateCounter;
 import org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditor;
 import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
 import org.apache.jackrabbit.oak.query.FilterIterators;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
-import org.apache.jackrabbit.oak.plugins.index.counter.ApproximateCounter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Supplier;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Queues;
 import com.google.common.collect.Sets;
@@ -335,7 +335,7 @@ public class ContentMirrorStoreStrategy
          * Keep the returned path, to avoid returning duplicate entries.
          */
         private final Set<String> knownPaths = Sets.newHashSet();
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
 
         PathIterator(Filter filter, String indexName, String pathPrefix) {
             this.filter = filter;
@@ -352,7 +352,7 @@ public class ContentMirrorStoreStrategy
             }            
             parentPath = "";
             currentPath = "/";
-            this.settings = filter.getQueryEngineSettings();
+            this.settings = filter.getQueryLimits();
         }
 
         void enqueue(Iterator<? extends ChildNodeEntry> it) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java Fri Aug 11 16:45:35 2017
@@ -121,7 +121,7 @@ class ReferenceIndex implements QueryInd
                 return lookup(root, uuid, name, WEAK_REF_NAME, filter);
             }
         }
-        return newPathCursor(new ArrayList<String>(), filter.getQueryEngineSettings());
+        return newPathCursor(new ArrayList<String>(), filter.getQueryLimits());
     }
 
     private Cursor lookup(NodeState root, String uuid,
@@ -129,7 +129,7 @@ class ReferenceIndex implements QueryInd
         NodeState indexRoot = root.getChildNode(INDEX_DEFINITIONS_NAME)
                 .getChildNode(NAME);
         if (!indexRoot.exists()) {
-            return newPathCursor(new ArrayList<String>(), filter.getQueryEngineSettings());
+            return newPathCursor(new ArrayList<String>(), filter.getQueryLimits());
         }
         List<Iterable<String>> iterables = Lists.newArrayList();
         for (IndexStoreStrategy s : getStrategies(indexRoot, mountInfoProvider, index)) {
@@ -152,7 +152,7 @@ class ReferenceIndex implements QueryInd
                 return getParentPath(path);
             }
         });
-        return newPathCursor(paths, filter.getQueryEngineSettings());
+        return newPathCursor(paths, filter.getQueryLimits());
     }
 
     private static Set<IndexStoreStrategy> getStrategies(NodeState definition,

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java Fri Aug 11 16:45:35 2017
@@ -20,8 +20,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
-
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,7 +42,7 @@ public class FilterIterators {
      * @param settings the query engine settings
      * @throws UnsupportedOperationException if the limit was exceeded
      */
-    public static void checkMemoryLimit(long count, QueryEngineSettings settings) {
+    public static void checkMemoryLimit(long count, QueryLimits settings) {
         long maxMemoryEntries = settings.getLimitInMemory();
         if (count > maxMemoryEntries) {
             String message = "The query read more than " + 
@@ -63,7 +62,7 @@ public class FilterIterators {
      * @param settings the query engine settings
      * @throws UnsupportedOperationException if the limit was exceeded
      */
-    public static void checkReadLimit(long count, QueryEngineSettings settings) {
+    public static void checkReadLimit(long count, QueryLimits settings) {
         long maxReadEntries = settings.getLimitReads();
         if (count > maxReadEntries) {
             String message = "The query read or traversed more than " + 
@@ -78,7 +77,7 @@ public class FilterIterators {
 
     public static <K> Iterator<K> newCombinedFilter(
             Iterator<K> it, boolean distinct, long limit, long offset, 
-            Comparator<K> orderBy, QueryEngineSettings settings) {
+            Comparator<K> orderBy, QueryLimits settings) {
         if (distinct) {
             it = FilterIterators.newDistinct(it, settings);
         }
@@ -98,7 +97,7 @@ public class FilterIterators {
         return it;
     }
     
-    public static <K> DistinctIterator<K> newDistinct(Iterator<K> it, QueryEngineSettings settings) {
+    public static <K> DistinctIterator<K> newDistinct(Iterator<K> it, QueryLimits settings) {
         return new DistinctIterator<K>(it, settings);
     }
     
@@ -110,7 +109,7 @@ public class FilterIterators {
         return new OffsetIterator<K>(it, offset);
     }
     
-    public static <K> Iterator<K> newSort(Iterator<K> it, Comparator<K> orderBy, int max, QueryEngineSettings settings) {
+    public static <K> Iterator<K> newSort(Iterator<K> it, Comparator<K> orderBy, int max, QueryLimits settings) {
         return new SortIterator<K>(it, orderBy, max, settings);
     }
 
@@ -124,12 +123,12 @@ public class FilterIterators {
     static class DistinctIterator<K> implements Iterator<K> {
 
         private final Iterator<K> source;
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
         private final HashSet<K> distinctSet;
         private K current;
         private boolean end;
 
-        DistinctIterator(Iterator<K> source, QueryEngineSettings settings) {
+        DistinctIterator(Iterator<K> source, QueryLimits settings) {
             this.source = source;
             this.settings = settings;
             distinctSet = new HashSet<K>();
@@ -188,12 +187,12 @@ public class FilterIterators {
     static class SortIterator<K> implements Iterator<K> {
 
         private final Iterator<K> source;
-        private final QueryEngineSettings settings;
+        private final QueryLimits settings;
         private final Comparator<K> orderBy;
         private Iterator<K> result;
         private final int max;
 
-        SortIterator(Iterator<K> source, Comparator<K> orderBy, int max, QueryEngineSettings settings) {
+        SortIterator(Iterator<K> source, Comparator<K> orderBy, int max, QueryLimits settings) {
             this.source = source;
             this.orderBy = orderBy;
             this.max = max;

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java Fri Aug 11 16:45:35 2017
@@ -26,6 +26,7 @@ import org.apache.jackrabbit.oak.api.Res
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
 
 /**
  * A "select" or "union" query.
@@ -206,5 +207,7 @@ public interface Query {
      *             fail in this case
      */
     void verifyNotPotentiallySlow();
+    
+    QueryExecutionStats getQueryExecutionStats();
 
 }

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=1804821&r1=1804820&r2=1804821&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 Fri Aug 11 16:45:35 2017
@@ -36,6 +36,7 @@ import org.apache.jackrabbit.oak.namepat
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.namepath.NamePathMapperImpl;
 import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
 import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.slf4j.Logger;
@@ -158,8 +159,9 @@ public abstract class QueryEngineImpl im
 
         NodeTypeInfoProvider nodeTypes = context.getNodeTypeInfoProvider();
         QueryEngineSettings settings = context.getSettings();
+        QueryExecutionStats stats = settings.getQueryStatsReporter().getQueryExecution(statement, language);
 
-        SQL2Parser parser = new SQL2Parser(mapper, nodeTypes, settings);
+        SQL2Parser parser = new SQL2Parser(mapper, nodeTypes, settings, stats);
         if (language.endsWith(NO_LITERALS)) {
             language = language.substring(0, language.length() - NO_LITERALS.length());
             parser.setAllowNumberLiterals(false);
@@ -192,6 +194,11 @@ public abstract class QueryEngineImpl im
         } else {
             throw new ParseException("Unsupported language: " + language, 0);
         }
+        if (q.isInternal()) {
+            stats.setInternal(true);
+        } else {
+            stats.setThreadName(Thread.currentThread().getName());
+        }
         
         queries.add(q);
         
@@ -267,7 +274,9 @@ public abstract class QueryEngineImpl im
 
         boolean mdc = false;
         try {
+            long start = System.nanoTime();
             Query query = prepareAndSelect(queries); 
+            query.getQueryExecutionStats().execute(System.nanoTime() - start);
             mdc = setupMDC(query);
             return query.executeQuery();
         } finally {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java Fri Aug 11 16:45:35 2017
@@ -36,6 +36,7 @@ import org.apache.jackrabbit.oak.api.Res
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.namepath.JcrPathParser;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
 import org.apache.jackrabbit.oak.query.QueryOptions.Traversal;
 import org.apache.jackrabbit.oak.query.ast.AndImpl;
 import org.apache.jackrabbit.oak.query.ast.AstVisitorBase;
@@ -79,8 +80,8 @@ import org.apache.jackrabbit.oak.query.i
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
 import org.apache.jackrabbit.oak.query.plan.ExecutionPlan;
 import org.apache.jackrabbit.oak.query.plan.SelectorExecutionPlan;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
 import org.apache.jackrabbit.oak.spi.query.Filter;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryConstants;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
@@ -111,6 +112,8 @@ public class QueryImpl implements Query
 
     private static final Logger LOG = LoggerFactory.getLogger(QueryImpl.class);
     
+    private final QueryExecutionStats stats;
+
     private boolean potentiallySlowTraversalQueryLogged;
 
     private static final Ordering<QueryIndex> MINIMAL_COST_ORDERING = new Ordering<QueryIndex>() {
@@ -176,13 +179,15 @@ public class QueryImpl implements Query
     private boolean potentiallySlowTraversalQuery;
 
     QueryImpl(String statement, SourceImpl source, ConstraintImpl constraint,
-        ColumnImpl[] columns, NamePathMapper mapper, QueryEngineSettings settings) {
+        ColumnImpl[] columns, NamePathMapper mapper, QueryEngineSettings settings,
+        QueryExecutionStats stats) {
         this.statement = statement;
         this.source = source;
         this.constraint = constraint;
         this.columns = columns;
         this.namePathMapper = mapper;
         this.settings = settings;
+        this.stats = stats;
     }
 
     @Override
@@ -801,6 +806,8 @@ public class QueryImpl implements Query
             if (end) {
                 return;
             }
+            long nanos = System.nanoTime();
+            long oldIndex = rowIndex;
             if (!started) {
                 source.execute(rootState);
                 started = true;
@@ -823,6 +830,8 @@ public class QueryImpl implements Query
                     break;
                 }
             }
+            nanos = System.nanoTime() - nanos;
+            stats.read(rowIndex - oldIndex, rowIndex, nanos);
         }
 
         @Override
@@ -1338,7 +1347,8 @@ public class QueryImpl implements Query
             this.constraint,
             cols.toArray(new ColumnImpl[0]),
             this.namePathMapper,
-            this.settings);
+            this.settings,
+            this.stats);
         copy.explain = this.explain;
         copy.distinct = this.distinct;
         
@@ -1364,4 +1374,8 @@ public class QueryImpl implements Query
         return queryOptions;
     }
 
+    public QueryExecutionStats getQueryExecutionStats() {
+        return stats;
+    }
+    
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java Fri Aug 11 16:45:35 2017
@@ -54,6 +54,7 @@ import org.apache.jackrabbit.oak.query.a
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
 import org.apache.jackrabbit.oak.query.ast.SourceImpl;
 import org.apache.jackrabbit.oak.query.ast.StaticOperandImpl;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.spi.query.QueryConstants;
@@ -114,6 +115,8 @@ public class SQL2Parser {
     
     private boolean literalUsageLogged;
 
+    private final QueryExecutionStats stats;
+
     /**
      * Create a new parser. A parser can be re-used, but it is not thread safe.
      * 
@@ -121,10 +124,12 @@ public class SQL2Parser {
      * @param nodeTypes the nodetypes
      * @param settings the query engine settings
      */
-    public SQL2Parser(NamePathMapper namePathMapper, NodeTypeInfoProvider nodeTypes, QueryEngineSettings settings) {
+    public SQL2Parser(NamePathMapper namePathMapper, NodeTypeInfoProvider nodeTypes, QueryEngineSettings settings,
+            QueryExecutionStats stats) {
         this.namePathMapper = namePathMapper;
         this.nodeTypes = checkNotNull(nodeTypes);
         this.settings = checkNotNull(settings);
+        this.stats = checkNotNull(stats);
     }
 
     /**
@@ -231,7 +236,7 @@ public class SQL2Parser {
             constraint = parseConstraint();
         }
         QueryImpl q = new QueryImpl(
-                statement, source, constraint, columnArray, namePathMapper, settings);
+                statement, source, constraint, columnArray, namePathMapper, settings, stats);
         q.setDistinct(distinct);
         return q;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java Fri Aug 11 16:45:35 2017
@@ -21,22 +21,22 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.Result.SizePrecision;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+import org.apache.jackrabbit.oak.query.QueryImpl.MeasuringIterator;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
-import org.apache.jackrabbit.oak.query.QueryImpl.MeasuringIterator;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterators;
+import com.google.common.collect.Maps;
 
 /**
  * Represents a union query.
@@ -408,5 +408,9 @@ public class UnionQueryImpl implements Q
         left.verifyNotPotentiallySlow();
         right.verifyNotPotentiallySlow();
     }
+    
+    public QueryExecutionStats getQueryExecutionStats() {
+        return left.getQueryExecutionStats();
+    }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Fri Aug 11 16:45:35 2017
@@ -426,6 +426,7 @@ public class SelectorImpl extends Source
     public boolean next() {
         while (cursor != null && cursor.hasNext()) {
             scanCount++;
+            query.getQueryExecutionStats().scan(1, scanCount);
             currentRow = cursor.next();
             if (isParent) {
                 // we must not check whether the _parent_ is readable

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java Fri Aug 11 16:45:35 2017
@@ -36,7 +36,7 @@ import com.google.common.collect.ArrayLi
 import com.google.common.collect.ListMultimap;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
 import org.apache.jackrabbit.oak.query.ast.JoinConditionImpl;
 import org.apache.jackrabbit.oak.query.ast.NativeFunctionImpl;
 import org.apache.jackrabbit.oak.query.ast.Operator;
@@ -57,7 +57,7 @@ public class FilterImpl implements Filte
     
     private final String queryStatement;
     
-    private final QueryEngineSettings settings;
+    private final QueryLimits settings;
 
     /**
      * Whether the filter is always false.
@@ -125,7 +125,19 @@ public class FilterImpl implements Filte
     }
     
     private FilterImpl() {
-        this(null, null, new QueryEngineSettings());
+        this(null, null, new QueryLimits() {
+
+            @Override
+            public long getLimitInMemory() {
+                return Long.MAX_VALUE;
+            }
+
+            @Override
+            public long getLimitReads() {
+                return Long.MAX_VALUE;
+            }
+            
+        });
     }
 
     /**
@@ -134,7 +146,7 @@ public class FilterImpl implements Filte
      * @param selector the selector for the given filter
      * @param queryStatement the query statement
      */
-    public FilterImpl(SelectorImpl selector, String queryStatement, QueryEngineSettings settings) {
+    public FilterImpl(SelectorImpl selector, String queryStatement, QueryLimits settings) {
         this.selector = selector;
         this.queryStatement = queryStatement;
         this.matchesAllTypes = selector != null ? selector.matchesAllTypes()
@@ -155,7 +167,7 @@ public class FilterImpl implements Filte
         this.selector = impl.selector;
         this.matchesAllTypes = selector != null ? selector.matchesAllTypes()
                 : false;
-        this.settings = filter.getQueryEngineSettings();
+        this.settings = filter.getQueryLimits();
     }
 
     public void setPreparing(boolean preparing) {
@@ -603,7 +615,7 @@ public class FilterImpl implements Filte
     }
 
     @Override
-    public QueryEngineSettings getQueryEngineSettings() {
+    public QueryLimits getQueryLimits() {
         return settings;
     }
 

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsData.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsData.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsData.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsData.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,179 @@
+/*
+ * 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.query.stats;
+
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+
+public class QueryStatsData {
+    
+    private final String query;
+    private final String language;
+    private final long createdMillis = System.currentTimeMillis();
+    private boolean internal;
+    private String lastThreadName;
+    private long lastExecutedMillis;
+    private long executeCount;
+    
+    /**
+     * Rows read by iterating over the query result.
+     */
+    private long totalRowsRead;
+    private long maxRowsRead;
+    
+    /**
+     *  Rows returned by the index.
+     */
+    private long totalRowsScanned;
+    private long maxRowsScanned;
+    private long planNanos;
+    private long readNanos;
+    private long maxTimeNanos;
+    private boolean captureStackTraces;
+
+    public QueryStatsData(String query, String language) {
+        this.query = query;
+        this.language = language;
+    }
+    
+    public String getKey() {
+        return query + "/" + language;
+    }
+    
+    /**
+     * The maximum CPU time needed to run one query.
+     * 
+     * @return the time in nanoseconds
+     */
+    public long getMaxTimeNanos() {
+        return maxTimeNanos;
+    }
+    
+    public long getTotalTimeNanos() {
+        return planNanos + readNanos;
+    }
+    
+    public long getMaxRowsScanned() {
+        return maxRowsScanned;
+    }
+    
+    public void setCaptureStackTraces(boolean captureStackTraces) {
+        this.captureStackTraces = captureStackTraces;
+    }
+    
+    public long getCreatedMillis() {
+        return createdMillis;
+    }
+    
+
+    public long getExecuteCount() {
+        return executeCount;
+    }
+
+    public long getTotalRowsRead() {
+        return totalRowsRead;
+    }
+
+    public long getTotalRowsScanned() {
+        return totalRowsScanned;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public String getQuery() {
+        return query;
+    }
+
+    public boolean isInternal() {
+        return internal;
+    }
+
+    public String getLastThreadName() {
+        return lastThreadName;
+    }
+
+    public long getLastExecutedMillis() {
+        return lastExecutedMillis;
+    }
+    
+    @Override
+    public String toString() {
+        return new JsopBuilder().object().
+            key("createdMillis").value(getTimeString(createdMillis)).
+            key("lastExecutedMillis").value(getTimeString(lastExecutedMillis)).
+            key("executeCount").value(executeCount).
+            key("totalRowsRead").value(totalRowsRead).
+            key("maxRowsRead").value(maxRowsRead).
+            key("totalRowsScanned").value(totalRowsScanned).
+            key("maxRowsScanned").value(maxRowsScanned).
+            key("planNanos").value(planNanos).
+            key("readNanos").value(readNanos).
+            key("maxTimeNanos").value(maxTimeNanos).
+            key("internal").value(internal).
+            key("query").value(query).
+            key("language").value(language).
+            key("lastThreadName").value(lastThreadName).
+        endObject().toString();
+    }
+    
+    public static final String getTimeString(long timeMillis) {
+        return String.format("%tF %tT", timeMillis, timeMillis);
+    }
+
+    public class QueryExecutionStats {
+        
+        long time;
+        
+        public void execute(long nanos) {
+            executeCount++;
+            lastExecutedMillis = System.currentTimeMillis();
+            time += nanos;
+            planNanos += nanos;
+            maxTimeNanos = Math.max(maxTimeNanos, time);
+        }
+
+        public void setInternal(boolean b) {
+            internal = b;
+        }
+
+        public void setThreadName(String name) {
+            if (captureStackTraces) {
+                StringBuilder buff = new StringBuilder();
+                for(StackTraceElement e : Thread.currentThread().getStackTrace()) {
+                    buff.append("\n\tat " + e);
+                }
+                name = name + buff.toString();
+            }
+            lastThreadName = name;
+        }
+
+        public void read(long count, long max, long nanos) {
+            totalRowsRead += count;
+            maxRowsRead = Math.max(maxRowsRead, max);
+            time += nanos;
+            readNanos += nanos;
+            maxTimeNanos = Math.max(maxTimeNanos, time);
+        }
+
+        public void scan(long count, long max) {
+            totalRowsScanned += count;
+            maxRowsScanned = Math.max(maxRowsScanned, max);
+        }        
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBean.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBean.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBean.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,58 @@
+/*
+ * 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.query.stats;
+
+import javax.management.openmbean.TabularData;
+
+import org.apache.jackrabbit.oak.api.jmx.Description;
+
+public interface QueryStatsMBean {
+
+    String TYPE = "QueryStats";
+    
+    /**
+     * Get the slow queries. Those are the ones that scan more than 100'000
+     * nodes, or the configured maximum number of nodes to scan. (Raw execution
+     * time is not taken into account, as execution can be slow if the code is
+     * not compiled yet.)
+     * 
+     * @return the slow queries table
+     */
+    @Description("Get the slow queries (those that scan/traverse over many nodes).")
+    TabularData getSlowQueries();
+    
+    @Description("Get the popular queries (those that take most of the time).")
+    TabularData getPopularQueries();
+
+    @Description("Get all data as Json.")
+    String asJson();
+   
+    @Description("Reset the statistics (clear the list of queries).")
+    void resetStats();
+
+    /**
+     * Whether to capture a thread dump in addition to the thread name.
+     * No thread name / thread dump is captures for internal queries.
+     * 
+     * @param captureStackTraces the new valu
+     */
+    @Description("Enable / disable capturing the thread dumps (in addition to the thread name).")
+    void setCaptureStackTraces(boolean captureStackTraces);
+    
+    boolean getCaptureStackTraces();
+    
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBeanImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBeanImpl.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBeanImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsMBeanImpl.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,229 @@
+/*
+ * 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.query.stats;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
+import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QueryStatsMBeanImpl extends AnnotatedStandardMBean 
+        implements QueryStatsMBean, QueryStatsReporter {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final int SLOW_QUERY_LIMIT_SCANNED = 
+            Integer.getInteger("oak.query.slowScanLimit", 100000);
+    private final int MAX_STATS_DATA = 
+            Integer.getInteger("oak.query.stats", 5000);
+    private final int MAX_POPULAR_QUERIES = 
+            Integer.getInteger("oak.query.slowLimit", 100);
+    private final ConcurrentSkipListMap<String, QueryStatsData> statistics = 
+            new ConcurrentSkipListMap<String, QueryStatsData>();
+    private final QueryEngineSettings settings;
+    private boolean captureStackTraces;
+    private int evictionCount;
+
+    public QueryStatsMBeanImpl(QueryEngineSettings settings) {
+        super(QueryStatsMBean.class);
+        this.settings = settings;
+    }
+    
+    @Override
+    public TabularData getSlowQueries() {
+        ArrayList<QueryStatsData> list = new ArrayList<QueryStatsData>();
+        long maxScanned = Math.min(SLOW_QUERY_LIMIT_SCANNED, settings.getLimitReads());
+        for(QueryStatsData s : statistics.values()) {
+            if(s.getMaxRowsScanned() > maxScanned) {
+                list.add(s);
+            }
+        }
+        Collections.sort(list, new Comparator<QueryStatsData>() {
+            @Override
+            public int compare(QueryStatsData o1, QueryStatsData o2) {
+                return -Long.compare(o1.getMaxTimeNanos(), o2.getMaxTimeNanos());
+            }
+        });
+        return asTabularData(list);
+    }
+    
+    @Override
+    public TabularData getPopularQueries() {
+        ArrayList<QueryStatsData> list = new ArrayList<QueryStatsData>(statistics.values());
+        Collections.sort(list, new Comparator<QueryStatsData>() {
+            @Override
+            public int compare(QueryStatsData o1, QueryStatsData o2) {
+                return -Long.compare(o1.getTotalTimeNanos(), o2.getTotalTimeNanos());
+            }
+        });
+        while (list.size() > MAX_POPULAR_QUERIES) {
+            list.remove(list.size() - 1);
+        }
+        return asTabularData(list);
+    }
+
+    @Override
+    public void resetStats() {
+        statistics.clear();
+    }
+    
+    @Override
+    public void setCaptureStackTraces(boolean captureStackTraces) {
+        this.captureStackTraces = captureStackTraces;
+    }
+
+    @Override
+    public boolean getCaptureStackTraces() {
+        return captureStackTraces;
+    }
+    
+    @Override
+    public String asJson() {
+        ArrayList<QueryStatsData> list = new ArrayList<QueryStatsData>(statistics.values());
+        Collections.sort(list, new Comparator<QueryStatsData>() {
+            @Override
+            public int compare(QueryStatsData o1, QueryStatsData o2) {
+                return -Long.compare(o1.getTotalTimeNanos(), o2.getTotalTimeNanos());
+            }
+        });
+        StringBuilder buff = new StringBuilder();
+        buff.append("[\n");
+        int i = 0;
+        for(QueryStatsData s : list) {
+            if (i++ > 0) {
+                buff.append(",\n");
+            }
+            buff.append(s.toString());
+        }
+        return buff.append("\n]\n").toString();
+    }
+
+    @Override
+    public QueryExecutionStats getQueryExecution(String statement, String language) {
+        if (log.isTraceEnabled()) {
+            log.trace("Execute " + language + " / " + statement);
+        }
+        if (statistics.size() > 2 * MAX_STATS_DATA) {
+            evict();
+        }
+        QueryStatsData stats = new QueryStatsData(statement, language);
+        QueryStatsData s2 = statistics.putIfAbsent(stats.getKey(), stats);
+        if (s2 != null) {
+            stats = s2;
+        }
+        stats.setCaptureStackTraces(captureStackTraces);
+        return stats.new QueryExecutionStats();
+    }
+
+    private void evict() {
+        evictionCount++;
+        // retain 50% of the slowest entries
+        // of the rest, retain the newest entries 
+        ArrayList<QueryStatsData> list = new ArrayList<QueryStatsData>(statistics.values());
+        Collections.sort(list, new Comparator<QueryStatsData>() {
+            @Override
+            public int compare(QueryStatsData o1, QueryStatsData o2) {
+                int comp = -Long.compare(o1.getTotalTimeNanos(), o2.getTotalTimeNanos());
+                if (comp == 0) {
+                    comp = -Long.compare(o1.getCreatedMillis(), o2.getCreatedMillis());
+                }
+                return comp;
+            }
+        });
+        Collections.sort(list.subList(MAX_STATS_DATA / 2, MAX_STATS_DATA), new Comparator<QueryStatsData>() {
+            @Override
+            public int compare(QueryStatsData o1, QueryStatsData o2) {
+                return -Long.compare(o1.getCreatedMillis(), o2.getCreatedMillis());
+            }
+        });
+        for (int i = MAX_STATS_DATA; i < list.size(); i++) {
+            statistics.remove(list.get(i).getKey());
+        }
+    }
+    
+    public int getEvictionCount() {
+        return evictionCount;
+    }
+    
+    private TabularData asTabularData(ArrayList<QueryStatsData> list) {
+        TabularDataSupport tds = null;
+        try {
+            CompositeType ct = QueryStatsCompositeTypeFactory.getCompositeType();
+            TabularType tt = new TabularType(QueryStatsData.class.getName(),
+                    "Query History", ct, QueryStatsCompositeTypeFactory.index);
+            tds = new TabularDataSupport(tt);
+            int position = 1;
+            for (QueryStatsData q : list) {
+                tds.put(new CompositeDataSupport(ct,
+                        QueryStatsCompositeTypeFactory.names,
+                        QueryStatsCompositeTypeFactory.getValues(q, position++)));
+            }
+            return tds;
+        } catch (Exception e) {
+            log.debug("Error", e);
+            return null;
+        }
+    }
+    
+    private static class QueryStatsCompositeTypeFactory {
+
+        private final static String[] index = { "position" };
+
+        private final static String[] names = { "position", 
+                "maxTimeMillis", "totalTimeMillis", "executeCount", 
+                "rowsRead", "rowsScanned", "maxRowsScanned",
+                "language", "statement", "lastExecuted",
+                "lastThread"};
+
+        private final static String[] descriptions = names;
+
+        @SuppressWarnings("rawtypes")
+        private final static OpenType[] types = { SimpleType.LONG,
+                SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, 
+                SimpleType.LONG, SimpleType.LONG, SimpleType.LONG, 
+                SimpleType.STRING, SimpleType.STRING, SimpleType.STRING,
+                SimpleType.STRING};
+
+        public static CompositeType getCompositeType() throws OpenDataException {
+            return new CompositeType(QueryStatsMBean.class.getName(),
+                    QueryStatsMBean.class.getName(), names, descriptions, types);
+        }
+
+        public static Object[] getValues(QueryStatsData q, int position) {
+            return new Object[] { (long) position,
+                    q.getMaxTimeNanos() / 1000000, q.getTotalTimeNanos() / 1000000, q.getExecuteCount(), 
+                    q.getTotalRowsRead(), q.getTotalRowsScanned(), q.getMaxRowsScanned(),
+                    q.getLanguage(), q.getQuery(), QueryStatsData.getTimeString(q.getLastExecutedMillis()),
+                    q.isInternal() ? "(internal query)" : q.getLastThreadName()};
+        }
+    }
+    
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsReporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsReporter.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsReporter.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/stats/QueryStatsReporter.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,25 @@
+/*
+ * 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.query.stats;
+
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData.QueryExecutionStats;
+
+public interface QueryStatsReporter {
+
+    QueryExecutionStats getQueryExecution(String statement, String language);
+    
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java Fri Aug 11 16:45:35 2017
@@ -68,7 +68,7 @@ public interface Filter {
      */
     FullTextExpression getFullTextConstraint();
 
-    QueryEngineSettings getQueryEngineSettings();
+    QueryLimits getQueryLimits();
 
     /**
      * check whether a certain (valid) path is accessible (can be read) from the user associated with the query Session
@@ -430,7 +430,7 @@ public interface Filter {
         }
 
         @Override
-        public QueryEngineSettings getQueryEngineSettings() {
+        public QueryEngineSettings getQueryLimits() {
             return EMPTY_SETTINGS;
         }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryEngineSettings.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryEngineSettings.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryEngineSettings.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryEngineSettings.java Fri Aug 11 16:45:35 2017
@@ -19,11 +19,14 @@
 package org.apache.jackrabbit.oak.spi.query;
 
 import org.apache.jackrabbit.oak.api.jmx.QueryEngineSettingsMBean;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsMBean;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsMBeanImpl;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsReporter;
 
 /**
  * Settings of the query engine.
  */
-public class QueryEngineSettings implements QueryEngineSettingsMBean {
+public class QueryEngineSettings implements QueryEngineSettingsMBean, QueryLimits {
     
     /**
      * the flag used to turn on/off the optimisations on top of the {@link org.apache.jackrabbit.oak.query.Query} object.
@@ -71,6 +74,8 @@ public class QueryEngineSettings impleme
     public static final boolean DEFAULT_FAST_QUERY_SIZE = Boolean.getBoolean(OAK_FAST_QUERY_SIZE);
     private boolean fastQuerySize = DEFAULT_FAST_QUERY_SIZE;
 
+    private QueryStatsMBeanImpl queryStats = new QueryStatsMBeanImpl(this);
+
     public QueryEngineSettings() {
     }
     
@@ -127,6 +132,14 @@ public class QueryEngineSettings impleme
         return sql2Optimisation;
     }
 
+    public QueryStatsMBean getQueryStats() {
+        return queryStats;
+    }
+    
+    public QueryStatsReporter getQueryStatsReporter() {
+        return queryStats;
+    }
+
     @Override
     public String toString() {
         return "QueryEngineSettings{" +

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryLimits.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryLimits.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryLimits.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryLimits.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,26 @@
+/*
+ * 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.spi.query;
+
+public interface QueryLimits {
+
+    long getLimitInMemory();
+
+    long getLimitReads();
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java Fri Aug 11 16:45:35 2017
@@ -17,18 +17,14 @@
 
 package org.apache.jackrabbit.oak.query;
 
-import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 
 import java.text.ParseException;
 
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
 import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.apache.jackrabbit.oak.spi.query.Filter;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -37,9 +33,7 @@ import org.junit.Test;
  */
 public class FilterTest {
     
-    private final NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
-
-    private final SQL2Parser p = new SQL2Parser(NamePathMapper.DEFAULT, nodeTypes, new QueryEngineSettings());
+    private final SQL2Parser p = SQL2ParserTest.createTestSQL2Parser();
 
     private Filter createFilter(String xpath) throws ParseException {
         String sql = new XPathToSQL2Converter().convert(xpath);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/LargeQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/LargeQueryTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/LargeQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/LargeQueryTest.java Fri Aug 11 16:45:35 2017
@@ -16,21 +16,18 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import java.text.ParseException;
 import java.util.Random;
 
-import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
 import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.junit.Test;
 
 public class LargeQueryTest {
     
-    private final NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
+    private final SQL2Parser parser = SQL2ParserTest.createTestSQL2Parser();
 
     @Test
     public void testSimpleOr() throws ParseException {
@@ -111,8 +108,7 @@ public class LargeQueryTest {
         sql2 = sql2.substring(0, xpathIndex);
         // should use union now
         assertTrue(sql2.indexOf(" or ") < 0);
-        SQL2Parser p = new SQL2Parser(null, nodeTypes, new QueryEngineSettings());
-        p.parse(sql2);
+        parser.parse(sql2);
     }
 
     private String randomCondition(Random r) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java Fri Aug 11 16:45:35 2017
@@ -27,9 +27,6 @@ import org.apache.jackrabbit.oak.api.Res
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.core.ImmutableRoot;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.junit.Test;
 
 public class NativeQueryTest {
@@ -37,9 +34,7 @@ public class NativeQueryTest {
     private final ImmutableRoot ROOT = new ImmutableRoot(INITIAL_CONTENT);
     private final QueryEngineImpl QUERY_ENGINE = (QueryEngineImpl)ROOT.getQueryEngine();
 
-    private final NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
-
-    private final SQL2Parser p = new SQL2Parser(NamePathMapper.DEFAULT, nodeTypes, new QueryEngineSettings());
+    private final SQL2Parser p = SQL2ParserTest.createTestSQL2Parser();
 
     
     @Test

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryCostOverheadTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryCostOverheadTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryCostOverheadTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryCostOverheadTest.java Fri Aug 11 16:45:35 2017
@@ -39,8 +39,8 @@ public class QueryCostOverheadTest {
         c1 = new ComparisonImpl(null, null, null);
         c2 = new FullTextSearchImpl(null, null, null);
         union = new UnionQueryImpl(false,
-                new QueryImpl(null, null, c1, null, null, null),
-                new QueryImpl(null, null, c2, null, null, null),
+                createQuery(c1),
+                createQuery(c2),
                 null);
         assertFalse("we always expect false from a `UnionQueryImpl`", 
                 union.containsUnfilteredFullTextCondition());
@@ -48,14 +48,14 @@ public class QueryCostOverheadTest {
         c1 = new ComparisonImpl(null, null, null);
         c2 = new FullTextSearchImpl(null, null, null);
         c = new OrImpl(c1, c2);
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertTrue(query.containsUnfilteredFullTextCondition());
 
         c1 = new ComparisonImpl(null, null, null);
         c2 = new FullTextSearchImpl(null, null, null);
         c3 = new FullTextSearchImpl(null, null, null);
         c = new OrImpl(of(c1, c2, c3));
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertTrue(query.containsUnfilteredFullTextCondition());
         
         c2 = new FullTextSearchImpl(null, null, null);
@@ -64,32 +64,35 @@ public class QueryCostOverheadTest {
         c1 = new OrImpl(of(c2, c3, c4));
         c5 = mock(DescendantNodeImpl.class);
         c = new AndImpl(c1, c5);
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertTrue(query.containsUnfilteredFullTextCondition());
         
         c = new FullTextSearchImpl(null, null, null);
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertFalse(query.containsUnfilteredFullTextCondition());
 
         c1 = new FullTextSearchImpl(null, null, null);
         c2 = new FullTextSearchImpl(null, null, null);
         c3 = new FullTextSearchImpl(null, null, null);
         c = new OrImpl(of(c1, c2, c3));
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertFalse(query.containsUnfilteredFullTextCondition());
         
         c1 = new ComparisonImpl(null, null, null);
         c2 = new FullTextSearchImpl(null, null, null);
         c3 = new FullTextSearchImpl(null, null, null);
         c = new AndImpl(of(c1, c2, c3));
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertFalse(query.containsUnfilteredFullTextCondition());
 
         c1 = new ComparisonImpl(null, null, null);
         c2 = new ComparisonImpl(null, null, null);
         c = new AndImpl(of(c1, c2, c3));
-        query = new QueryImpl(null, null, c, null, null, null);
+        query = createQuery(c);
         assertFalse(query.containsUnfilteredFullTextCondition());
-
+    }
+    
+    QueryImpl createQuery(ConstraintImpl c) {
+        return new QueryImpl(null, null, c, null, null, null, null);
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java Fri Aug 11 16:45:35 2017
@@ -188,7 +188,8 @@ public class SQL2OptimiseQueryTest exten
      */
     @Test
     public void optimise() throws ParseException {
-        SQL2Parser parser = new SQL2Parser(getMappings(), getNodeTypes(), qeSettings);
+        SQL2Parser parser = SQL2ParserTest.createTestSQL2Parser(
+                getMappings(), getNodeTypes(), qeSettings);
         String statement;
         Query original, optimised;
 
@@ -262,7 +263,8 @@ public class SQL2OptimiseQueryTest exten
     }
     
     private void optimiseAndOrAnd(String statement, String expected) throws ParseException {
-        SQL2Parser parser = new SQL2Parser(getMappings(), getNodeTypes(), qeSettings);
+        SQL2Parser parser = SQL2ParserTest.createTestSQL2Parser(
+                getMappings(), getNodeTypes(), qeSettings);
         Query original;
         original = parser.parse(statement, false);
         assertNotNull(original);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java Fri Aug 11 16:45:35 2017
@@ -21,7 +21,12 @@ import static org.junit.Assert.assertTru
 
 import java.text.ParseException;
 
+import org.apache.jackrabbit.oak.api.QueryEngine;
+import org.apache.jackrabbit.oak.namepath.LocalNameMapper;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.namepath.NamePathMapperImpl;
 import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.stats.QueryStatsData;
 import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.junit.Test;
@@ -31,9 +36,21 @@ import org.junit.Test;
  */
 public class SQL2ParserTest {
 
-    private final NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
+    private static final NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
+
+    private static final SQL2Parser p = createTestSQL2Parser();
+    
+    public static SQL2Parser createTestSQL2Parser() {
+        return createTestSQL2Parser(NamePathMapper.DEFAULT, nodeTypes, new QueryEngineSettings());
+    }
+    
+    public static SQL2Parser createTestSQL2Parser(NamePathMapper mappings, NodeTypeInfoProvider nodeTypes2,
+            QueryEngineSettings qeSettings) {
+        QueryStatsData data = new QueryStatsData("", "");
+        return new SQL2Parser(mappings, nodeTypes2, new QueryEngineSettings(), 
+                data.new QueryExecutionStats());
+    }
 
-    private final SQL2Parser p = new SQL2Parser(null, nodeTypes, new QueryEngineSettings());
 
     @Test
     public void testIgnoreSqlComment() throws ParseException {
@@ -70,5 +87,5 @@ public class SQL2ParserTest {
         String token = "and b.[type] in('t1', 't2', 't3')";
         assertTrue(q.contains(token));
     }
-    
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/XPathTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/XPathTest.java?rev=1804821&r1=1804820&r2=1804821&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/XPathTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/XPathTest.java Fri Aug 11 16:45:35 2017
@@ -16,16 +16,13 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.text.ParseException;
 
-import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
 import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
-import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
 import org.junit.Test;
 
 /**
@@ -33,8 +30,7 @@ import org.junit.Test;
  */
 public class XPathTest {
     
-    private final NodeTypeInfoProvider nodeTypes =
-            new NodeStateNodeTypeInfoProvider(INITIAL_CONTENT);
+    private final SQL2Parser parser = SQL2ParserTest.createTestSQL2Parser();
     
     @Test
     public void complexQuery() throws ParseException {
@@ -65,8 +61,7 @@ public class XPathTest {
         String xpath = buff.toString();
         String sql2 = new XPathToSQL2Converter().convert(xpath);
         assertTrue("Length: " + sql2.length(), sql2.length() < 200000);
-        SQL2Parser p = new SQL2Parser(null, nodeTypes, new QueryEngineSettings());
-        Query q = p.parse(sql2, false);
+        Query q = parser.parse(sql2, false);
         q.buildAlternativeQuery();
     }
     
@@ -321,8 +316,7 @@ public class XPathTest {
         sql2 = formatSQL(sql2);
         expectedSql2 = formatSQL(expectedSql2);
         assertEquals(expectedSql2, sql2);
-        SQL2Parser p = new SQL2Parser(null, nodeTypes, new QueryEngineSettings());
-        p.parse(sql2);
+        parser.parse(sql2);
     }
     
     static String formatSQL(String sql) {

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/QueryStatsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/QueryStatsTest.java?rev=1804821&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/QueryStatsTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/stats/QueryStatsTest.java Fri Aug 11 16:45:35 2017
@@ -0,0 +1,59 @@
+/*
+ * 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.query.stats;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.oak.spi.query.QueryEngineSettings;
+import org.junit.Test;
+
+public class QueryStatsTest {
+
+    @Test
+    public void testEviction() throws InterruptedException {
+        QueryStatsMBeanImpl bean = new QueryStatsMBeanImpl(new QueryEngineSettings());
+        for (int i = 0; i < 10010; i++) {
+            bean.getQueryExecution("old" + i, "");
+            if (i % 100 == 0) {
+                Thread.sleep(1);
+            }
+        }
+        assertEquals(1, bean.getEvictionCount());
+        // remain around 5000
+        
+        Thread.sleep(5);
+        for (int i = 0; i < 10; i++) {
+            bean.getQueryExecution("slow" + i, "").execute(10000);
+        }
+        Thread.sleep(5);
+        
+        assertEquals(1, bean.getEvictionCount());
+        for (int i = 0; i < 10010; i++) {
+            bean.getQueryExecution("new" + i, "");
+            if (i % 100 == 0) {
+                Thread.sleep(1);
+            }
+        }
+        assertEquals(3, bean.getEvictionCount());
+        String json = bean.asJson();
+        for (int i = 0; i < 10; i++) {
+            assertTrue(json.indexOf("slow" + i) >= 0);
+        }
+        assertTrue(json.indexOf("old") < 0);
+    }
+}