[maven-release-plugin]  copy for tag jackrabbit-oak-1.4.2

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/tags/jackrabbit-oak-1.4.2@1742123 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 20471bd..30a7a44 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -24,8 +24,14 @@
 
 Bug
 
+    [OAK-4099] - Lucene index appear to be corrupted with compaction
+    enabled
     [OAK-4148] - RAT plugin complains about derby files
     [OAK-4166] - Simple versionable nodes are invalid after migration
+    [OAK-4275] - Backport OAK-4065 (Counter index can get out of sync)
+    to 1.2 and 1.4
+    [OAK-4313] - QueryImpl should avoid traversal with queries
+    containing native constraints
 
 Documentation
 
@@ -35,10 +41,14 @@
 Improvement
 
     [OAK-4136] - release profile in maven
+    [OAK-4165] - Too verbose logging during revision gc
     [OAK-4175] - Allow to skip the repository initialization in
     oak-upgrade
     [OAK-4242] - Counter index should be rebuilt after successful
     migration
+    [OAK-4306] - Disable cleanup when compaction is paused
+    [OAK-4317] - Similar and Native queries should return no results
+    if no index can handle them
 
 Task
 
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
index d5a4590..6e2633a 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
@@ -24,10 +24,12 @@
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.util.ApproximateCounter;
+import org.apache.jackrabbit.oak.util.SipHash;
 
 /**
  * An approximate descendant node counter mechanism.
@@ -35,18 +37,40 @@
 public class NodeCounterEditor implements Editor {
 
     public static final String DATA_NODE_NAME = ":index";
+    
+    // the property that is used with the "old" (pseudo-random number generator based) method
     public static final String COUNT_PROPERTY_NAME = ":count";
+    
+    // the property that is used with the "new" (hash of the path based) method
+    public static final String COUNT_HASH_PROPERTY_NAME = ":cnt";
+    
     public static final int DEFAULT_RESOLUTION = 1000;
     
     private final NodeCounterRoot root;
     private final NodeCounterEditor parent;
     private final String name;
     private long countOffset;
+    private SipHash hash;
     
-    public NodeCounterEditor(NodeCounterRoot root, NodeCounterEditor parent, String name) {
+    public NodeCounterEditor(NodeCounterRoot root, NodeCounterEditor parent, String name, SipHash hash) {
         this.parent = parent;
         this.root = root;
         this.name = name;
+        this.hash = hash;
+    }    
+    
+    private SipHash getHash() {
+        if (hash != null) {
+            return hash;
+        }
+        SipHash h;
+        if (parent == null) {
+            h = new SipHash(root.seed);
+        } else {
+            h = new SipHash(parent.getHash(), name.hashCode());
+        }
+        this.hash = h;
+        return h;
     }
 
     @Override
@@ -58,6 +82,15 @@
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
+        if (NodeCounter.COUNT_HASH) {
+            leaveNew(before, after);
+            return;
+        }
+        leaveOld(before, after);
+    }
+    
+    private void leaveOld(NodeState before, NodeState after)
+            throws CommitFailedException {
         long offset = ApproximateCounter.calculateOffset(
                 countOffset, root.resolution);
         if (offset == 0) {
@@ -84,6 +117,27 @@
             builder.setProperty(COUNT_PROPERTY_NAME, count);
         }
     }
+    
+    public void leaveNew(NodeState before, NodeState after)
+            throws CommitFailedException {        
+        if (countOffset == 0) {
+            return;
+        }
+        NodeBuilder builder = getBuilder();
+        PropertyState p = builder.getProperty(COUNT_HASH_PROPERTY_NAME);
+        long count = p == null ? 0 : p.getValue(Type.LONG);
+        count += countOffset;
+        root.callback.indexUpdate();
+        if (count <= 0) {
+            if (builder.getChildNodeCount(1) >= 0) {
+                builder.removeProperty(COUNT_HASH_PROPERTY_NAME);
+            } else {
+                builder.remove();
+            }
+        } else {
+            builder.setProperty(COUNT_HASH_PROPERTY_NAME, count);
+        }
+    }
 
     private NodeBuilder getBuilder() {
         if (parent == null) {
@@ -113,23 +167,41 @@
     @CheckForNull
     public Editor childNodeChanged(String name, NodeState before, NodeState after)
             throws CommitFailedException {
-        return getChildIndexEditor(this, name);
+        return getChildIndexEditor(name, null);
     }
 
     @Override
     @CheckForNull
     public Editor childNodeAdded(String name, NodeState after)
             throws CommitFailedException {
+        if (NodeCounter.COUNT_HASH) {
+            SipHash h = new SipHash(getHash(), name.hashCode());
+            // with bitMask=1024: with a probability of 1:1024,
+            if ((h.hashCode() & root.bitMask) == 0) {
+                // add 1024
+                count(root.bitMask + 1);
+            }
+            return getChildIndexEditor(name, h);            
+        }
         count(1);
-        return getChildIndexEditor(this, name);
+        return getChildIndexEditor(name, null);
     }
 
     @Override
     @CheckForNull
     public Editor childNodeDeleted(String name, NodeState before)
             throws CommitFailedException {
+        if (NodeCounter.COUNT_HASH) {
+            SipHash h = new SipHash(getHash(), name.hashCode());
+            // with bitMask=1024: with a probability of 1:1024,
+            if ((h.hashCode() & root.bitMask) == 0) {
+                // subtract 1024                
+                count(-(root.bitMask + 1));
+            }
+            return getChildIndexEditor(name, h);
+        }
         count(-1);
-        return getChildIndexEditor(this, name);
+        return getChildIndexEditor(name, null);
     }
     
     private void count(int offset) {
@@ -139,16 +211,27 @@
         }
     }
     
-    private Editor getChildIndexEditor(NodeCounterEditor nodeCounterEditor,
-            String name) {
-        return new NodeCounterEditor(root, this, name);
+    private Editor getChildIndexEditor(String name, SipHash hash) {
+        return new NodeCounterEditor(root, this, name, hash);
     }
     
     public static class NodeCounterRoot {
-        int resolution = DEFAULT_RESOLUTION;
-        NodeBuilder definition;
-        NodeState root;
-        IndexUpdateCallback callback;
+        final int resolution;
+        final long seed;
+        final int bitMask;
+        final NodeBuilder definition;
+        final NodeState root;
+        final IndexUpdateCallback callback;
+        
+        NodeCounterRoot(int resolution, long seed, NodeBuilder definition, NodeState root, IndexUpdateCallback callback) {
+            this.resolution = resolution;
+            this.seed = seed;
+            // if resolution is 1000, then the bitMask is 1023 (bits 0..9 set)
+            this.bitMask = (Integer.highestOneBit(resolution) * 2) - 1;
+            this.definition = definition;
+            this.root = root;
+            this.callback = callback;
+        }
     }
 
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java
index 13fbbf8..09aca62 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.counter;
 
+import java.util.UUID;
+
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
@@ -29,6 +31,7 @@
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
 import org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditor.NodeCounterRoot;
+import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -41,6 +44,8 @@
 
     public static final String RESOLUTION = "resolution";
 
+    public static final String SEED = "seed";
+
     @Override
     @CheckForNull
     public Editor getIndexEditor(@Nonnull String type,
@@ -49,15 +54,28 @@
         if (!TYPE.equals(type)) {
             return null;
         }
-        NodeCounterRoot rootData = new NodeCounterRoot();
-        rootData.callback = callback;
-        rootData.definition = definition;
-        rootData.root = root;
+        int resolution; 
         PropertyState s = definition.getProperty(RESOLUTION);
-        if (s != null) {
-            rootData.resolution = s.getValue(Type.LONG).intValue();
+        if (s == null) {
+            resolution = NodeCounterEditor.DEFAULT_RESOLUTION; 
+        } else {
+            resolution = s.getValue(Type.LONG).intValue();
         }
-        return new NodeCounterEditor(rootData, null, "/");
+        long seed;
+        s = definition.getProperty(SEED);
+        if (s != null) {
+            seed = s.getValue(Type.LONG).intValue();
+        } else {
+            seed = 0;
+            if (NodeCounter.COUNT_HASH) {
+                // create a random number (that way we can also check if this feature is enabled)
+                seed = UUID.randomUUID().getMostSignificantBits();
+                definition.setProperty(SEED, seed);
+            }
+        }
+        NodeCounterRoot rootData = new NodeCounterRoot(
+                resolution, seed, definition, root, callback);
+        return new NodeCounterEditor(rootData, null, "/", null);
     }
 
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
index 10954f0..3714501 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
@@ -37,6 +37,13 @@
  */
 public class NodeCounter implements NodeCounterMBean {
     
+    /**
+     * Approximate count using the hashed name (deterministically, so that after
+     * adding a removing all nodes the count goes back to zero).
+     */
+    public final static boolean COUNT_HASH = 
+            Boolean.parseBoolean(System.getProperty("oak.countHashed", "true"));
+    
     private final NodeStore store;
     
     public NodeCounter(NodeStore store) {
@@ -90,6 +97,14 @@
                 return syncCount;
             }
         }
+        if (COUNT_HASH) {
+            return getCombinedCount(root, path, s, max);
+        }
+        return getEstimatedNodeCountOld(root, s, path, max);
+    }
+    
+    private static long getEstimatedNodeCountOld(NodeState root, NodeState s, String path, boolean max) {
+        // old code from here
         PropertyState p = s.getProperty(NodeCounterEditor.COUNT_PROPERTY_NAME);
         if (p != null) {
             long x = p.getValue(Type.LONG);
@@ -135,6 +150,47 @@
         return x;
     }
     
+    private static long getCombinedCount(NodeState root, String path, NodeState s, boolean max) {
+        Long value = getCombinedCountIfAvailable(s);
+        if (value != null) {
+            return value + (max ? ApproximateCounter.COUNT_RESOLUTION : 0);
+        }
+        // check in the counter index (if it exists)
+        s = child(root,
+                IndexConstants.INDEX_DEFINITIONS_NAME,
+                "counter");
+        if (s == null || !s.exists()) {
+            // no index
+            return -1;
+        }
+        s = child(s, NodeCounterEditor.DATA_NODE_NAME);
+        s = child(s, PathUtils.elements(path));
+        if (s != null && s.exists()) {
+            value = getCombinedCountIfAvailable(s);
+            if (value != null) {
+                return value + (max ? ApproximateCounter.COUNT_RESOLUTION : 0);
+            }
+        }
+        // we have an index, but no data
+        return max ? ApproximateCounter.COUNT_RESOLUTION * 20 : 0;
+    }
+    
+    private static Long getCombinedCountIfAvailable(NodeState s) {
+        boolean found = false;
+        long x = 0;
+        PropertyState p = s.getProperty(NodeCounterEditor.COUNT_HASH_PROPERTY_NAME);
+        if (p != null) {
+            found = true;
+            x = p.getValue(Type.LONG);
+        }
+        p = s.getProperty(NodeCounterEditor.COUNT_PROPERTY_NAME);
+        if (p != null) {
+            found = true;
+            x += p.getValue(Type.LONG);
+        }
+        return found ? x : null;
+    }
+    
     @Override
     public String getEstimatedChildNodeCounts(String path, int level) {
         StringBuilder buff = new StringBuilder();
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
index 6af18c2..2e4a7e3 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
@@ -23,6 +23,8 @@
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static org.apache.jackrabbit.oak.spi.query.QueryIndex.NativeQueryIndex;
 
@@ -30,7 +32,9 @@
  * A native function condition.
  */
 public class NativeFunctionImpl extends ConstraintImpl {
-    
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
     public static final String NATIVE_PREFIX = "native*";
     
     private final String selectorName;
@@ -63,7 +67,8 @@
         // and because we don't know how to process native
         // conditions
         if (!(selector.getIndex() instanceof NativeQueryIndex)) {
-            throw new IllegalArgumentException("No full-text index was found that can process the condition " + toString());
+            log.warn("No full-text index was found that can process the condition " + toString());
+            return false;
         }
         // we assume the index only returns the requested entries
         return true;
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java
index d85b3e6..b96839d 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java
@@ -26,12 +26,16 @@
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex.FulltextQueryIndex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Support for "similar(...)
  */
 public class SimilarImpl extends ConstraintImpl {
-    
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
     public static final String NATIVE_LUCENE_LANGUAGE = "lucene";
     
     public static final String MORE_LIKE_THIS_PREFIX = "mlt?mlt.fl=:path&mlt.mindf=0&stream.body=";
@@ -67,7 +71,8 @@
         // and because we don't know how to process native
         // conditions
         if (!(selector.getIndex() instanceof FulltextQueryIndex)) {
-            throw new IllegalArgumentException("No full-text index was found that can process the condition " + toString());
+            log.warn("No full-text index was found that can process the condition " + toString());
+            return false;
         }
         // verify the path is readable
         PropertyValue p = pathExpression.currentValue();
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java
index 149fe52..dcf13de 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java
@@ -241,6 +241,12 @@
             NodeState parent = null;
             NodeState node = rootState;
             
+            if (filter.containsNativeConstraint()) {
+                // OAK-4313: if no other index was found,
+                // then, for native queries, we won't match anything
+                return;
+            }
+
             if (filter.isAlwaysFalse()) {
                 // nothing can match this filter, leave nodes empty
                 return;
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/util/SipHash.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/util/SipHash.java
new file mode 100644
index 0000000..207dbb4
--- /dev/null
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/util/SipHash.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util;
+
+/**
+ * An implementation of the SipHash-2-2 function, to prevent hash flooding.
+ */
+public class SipHash {
+    
+    private final long v0, v1, v2, v3;
+
+    public SipHash(long seed) {
+        long k0 = seed;
+        long k1 = Long.rotateLeft(seed, 32);
+        v0 = k0 ^ 0x736f6d6570736575L;
+        v1 = k1 ^ 0x646f72616e646f6dL;
+        v2 = k0 ^ 0x6c7967656e657261L;
+        v3 = k1 ^ 0x7465646279746573L;
+    }
+
+    public SipHash(SipHash parent, long m) {
+        long v0 = parent.v0;
+        long v1 = parent.v1;
+        long v2 = parent.v2;
+        long v3 = parent.v3;
+        int repeat = 2;
+        for (int i = 0; i < repeat; i++) {
+            v0 += v1;
+            v2 += v3;
+            v1 = Long.rotateLeft(v1, 13);
+            v3 = Long.rotateLeft(v3, 16);
+            v1 ^= v0;
+            v3 ^= v2;
+            v0 = Long.rotateLeft(v0, 32);
+            v2 += v1;
+            v0 += v3;
+            v1 = Long.rotateLeft(v1, 17);
+            v3 = Long.rotateLeft(v3, 21);
+            v1 ^= v2;
+            v3 ^= v0;
+            v2 = Long.rotateLeft(v2, 32);
+        }
+        v0 ^= m;      
+        this.v0 = v0;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v3= v3;
+    }
+    
+    @Override
+    public int hashCode() {
+        long x = v0 ^ v1 ^ v2 ^ v3;
+        return (int) (x ^ (x >>> 16));
+    }
+
+}
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java
new file mode 100644
index 0000000..6ead45b
--- /dev/null
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/query/NativeQueryTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.api.Result;
+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.spi.state.NodeState;
+import org.junit.Test;
+
+import java.text.ParseException;
+import java.util.Iterator;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
+import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class NativeQueryTest {
+
+    private final NodeState types = INITIAL_CONTENT.getChildNode(JCR_SYSTEM)
+            .getChildNode(JCR_NODE_TYPES);
+
+    private final ImmutableRoot ROOT = new ImmutableRoot(INITIAL_CONTENT);
+    private final QueryEngineImpl QUERY_ENGINE = (QueryEngineImpl)ROOT.getQueryEngine();
+
+    private final SQL2Parser p = new SQL2Parser(NamePathMapper.DEFAULT, types, new QueryEngineSettings());
+
+    @Test
+    public void dontTraverseForSuggest() throws Exception {
+        String sql = "select [rep:suggest()] from [nt:base] where suggest('test')";
+        assertDontTraverseFor(sql);
+    }
+
+    @Test
+    public void dontTraverseForSpellcheck() throws Exception {
+        String sql = "select [rep:spellcheck()] from [nt:base] where spellcheck('test')";
+        assertDontTraverseFor(sql);
+    }
+
+    @Test
+    public void dontTraverseForNative() throws Exception {
+        String sql = "select [jcr:path] from [nt:base] where native('solr', 'name:(Hello OR World)')";
+        assertDontTraverseFor(sql);
+    }
+
+    @Test
+    public void dontTraverseForSimilar() throws Exception {
+        String sql = "select [rep:similar()] from [nt:base] where similar(., '/test/a')";
+        assertDontTraverseFor(sql);
+    }
+
+    private void assertDontTraverseFor(String sql) throws ParseException {
+        QueryImpl query = (QueryImpl)p.parse(sql);
+        query.setExecutionContext(QUERY_ENGINE.getExecutionContext());
+        Result result = query.executeQuery();
+        Iterator<? extends ResultRow> it = result.getRows().iterator();
+        assertFalse("Zero results expected", it.hasNext());
+
+        query = (QueryImpl)p.parse("measure " + sql);
+        query.setExecutionContext(QUERY_ENGINE.getExecutionContext());
+        result = query.executeQuery();
+        it = result.getRows().iterator();
+        while(it.hasNext()) {
+            ResultRow row = it.next();
+            String selector = row.getValue("selector").getValue(Type.STRING);
+            if ("nt:base".equals(selector)) {
+                long scanCount = row.getValue("scanCount").getValue(Type.LONG);
+                // we expect that no was scanned that's it
+                // - no traversal of the whole respository
+                assertEquals("Repository's scan count doesn't match", 0, scanCount);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_native.txt b/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_native.txt
index df92034..ec26087 100644
--- a/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_native.txt
+++ b/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_native.txt
@@ -28,15 +28,12 @@
 
 select [jcr:path] from [nt:base]
   where native('solr', 'name:(Hello OR World)')
-java.lang.IllegalArgumentException: No full-text index was found that can process the condition native([nt:base], [solr], 'name:(Hello OR World)')
 
 select [jcr:path] from [nt:base] as a
   where native(a, 'solr', 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
-java.lang.IllegalArgumentException: No full-text index was found that can process the condition native([a], [solr], 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
 
 select [jcr:path] from [nt:base]
   where native('solr', 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
-java.lang.IllegalArgumentException: No full-text index was found that can process the condition native([nt:base], [solr], 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
 
 xpath2sql //*[rep:native('solr', 'xyz')]
 select [jcr:path], [jcr:score], *
diff --git a/oak-doc/pom.xml b/oak-doc/pom.xml
index 5966514..f84f807 100644
--- a/oak-doc/pom.xml
+++ b/oak-doc/pom.xml
@@ -23,7 +23,7 @@
   <parent>
     <groupId>org.apache.jackrabbit</groupId>
     <artifactId>oak-parent</artifactId>
-    <version>1.4.0-SNAPSHOT</version>
+    <version>1.4.2-SNAPSHOT</version>
     <relativePath>../oak-parent/pom.xml</relativePath>
   </parent>
 
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
index c815095..b0a0e06 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
@@ -61,6 +61,8 @@
 
     private volatile Map<String, IndexNode> indices = emptyMap();
 
+    private volatile boolean refresh;
+
     IndexTracker() {
         this(null);
     }
@@ -83,6 +85,17 @@
     }
 
     synchronized void update(final NodeState root) {
+        if (refresh) {
+            this.root = root;
+            close();
+            refresh = false;
+            log.info("Refreshed the opened indexes");
+        } else {
+            diffAndUpdate(root);
+        }
+    }
+
+    private synchronized void diffAndUpdate(final NodeState root) {
         Map<String, IndexNode> original = indices;
         final Map<String, IndexNode> updates = newHashMap();
 
@@ -129,6 +142,10 @@
         }
     }
 
+    void refresh() {
+        refresh = true;
+    }
+
     IndexNode acquireIndexNode(String path) {
         IndexNode index = indices.get(path);
         if (index != null && index.acquire()) {
@@ -177,5 +194,4 @@
 
         return null;
     }
-
 }
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderService.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderService.java
index 64b97f7..ef24bc4 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderService.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProviderService.java
@@ -35,7 +35,6 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -56,6 +55,7 @@
 import org.apache.jackrabbit.oak.plugins.index.lucene.score.ScorerProviderFactory;
 import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
@@ -70,6 +70,7 @@
 import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.emptyMap;
 import static org.apache.commons.io.FileUtils.ONE_MB;
 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
 
@@ -242,6 +243,7 @@
                 new LuceneIndexMBeanImpl(indexProvider.getTracker()),
                 LuceneIndexMBean.TYPE,
                 "Lucene Index statistics"));
+        registerGCMonitor(whiteboard, indexProvider.getTracker());
     }
 
     @Deactivate
@@ -488,6 +490,17 @@
         }
     }
 
+    private void registerGCMonitor(Whiteboard whiteboard,
+            final IndexTracker tracker) {
+        GCMonitor gcMonitor = new GCMonitor.Empty() {
+            @Override
+            public void compacted(long[] segmentCounts, long[] recordCounts,
+                    long[] compactionMapWeights) {
+                tracker.refresh();
+            }
+        };
+        oakRegs.add(whiteboard.register(GCMonitor.class, gcMonitor, emptyMap()));
+    }
 
     protected void bindNodeAggregator(NodeAggregator aggregator) {
         this.nodeAggregator = aggregator;
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTrackerTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTrackerTest.java
new file mode 100644
index 0000000..e4f0908
--- /dev/null
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTrackerTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.index.lucene;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
+import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static org.junit.Assert.assertEquals;
+
+public class IndexTrackerTest {
+    private static final EditorHook HOOK = new EditorHook(
+            new IndexUpdateProvider(
+                    new LuceneIndexEditorProvider()));
+
+    private NodeState root = INITIAL_CONTENT;
+
+    private NodeBuilder builder = root.builder();
+
+    private IndexTracker tracker = new IndexTracker();
+
+    @Test
+    public void update() throws Exception{
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        newLucenePropertyIndexDefinition(index, "lucene", ImmutableSet.of("foo"), null);
+
+        NodeState before = builder.getNodeState();
+        builder.setProperty("foo", "bar");
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+
+        assertEquals(0, tracker.getIndexNodePaths().size());
+
+        tracker.update(indexed);
+        IndexNode indexNode = tracker.acquireIndexNode("/oak:index/lucene");
+        indexNode.release();
+        assertEquals(1, tracker.getIndexNodePaths().size());
+
+        tracker.refresh();
+        assertEquals(1, tracker.getIndexNodePaths().size());
+
+        tracker.update(indexed);
+        //Post refresh size should be 0 as all are closed
+        assertEquals(0, tracker.getIndexNodePaths().size());
+    }
+
+}
\ No newline at end of file
diff --git a/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java b/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
index 9e26f2b..86387f9 100644
--- a/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
+++ b/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
@@ -584,7 +584,7 @@
                     humanReadableByteCount(needed), needed,
                     humanReadableByteCount(delta), delta);
             if (cleanup) {
-                cleanupNeeded.set(true);
+                cleanupNeeded.set(!compactionStrategy.isPaused());
             }
             return false;
         }
@@ -648,7 +648,7 @@
             }
         }
         if (cleanup) {
-            cleanupNeeded.set(true);
+            cleanupNeeded.set(!compactionStrategy.isPaused());
         }
         return compacted;
     }
@@ -964,7 +964,8 @@
                 }
             }
             if (!fRefs.isEmpty()) {
-                gcMonitor.info("TarMK GC #{}: cleanup found forward references to {}", gcCount, fRefs);
+                gcMonitor.info("TarMK GC #{}: cleanup found {} forward references", gcCount, fRefs.size());
+                log.debug("TarMK GC #{}: cleanup found forward references to {}", gcCount, fRefs);
             }
             // ... as long as new forward references are found.
         } while (referencedIds.addAll(fRefs));