[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));