blob: 96783fcad6a9ad3dbae56590ca9b7a9d5d87c333 [file] [log] [blame]
/*
* 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 java.io.File;
import java.io.IOException;
import java.util.Collections;
import com.google.common.collect.ImmutableList;
import org.apache.jackrabbit.oak.plugins.index.lucene.directory.OakDirectory;
import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.NRTIndex;
import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.NRTIndexFactory;
import org.apache.jackrabbit.oak.plugins.index.lucene.reader.DefaultIndexReaderFactory;
import org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReaderFactory;
import org.apache.jackrabbit.oak.plugins.index.lucene.writer.IndexWriterUtils;
import org.apache.jackrabbit.oak.plugins.index.search.spi.query.IndexNodeManager;
import org.apache.jackrabbit.oak.plugins.index.search.update.ReaderRefreshPolicy;
import org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
import org.apache.jackrabbit.oak.spi.mount.Mounts;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.stats.StatisticsProvider;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static org.apache.jackrabbit.oak.api.Type.STRINGS;
import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.newDoc;
import static org.apache.jackrabbit.oak.InitialContentHelper.INITIAL_CONTENT;
import static org.apache.jackrabbit.oak.plugins.index.search.FieldNames.PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
public class IndexNodeManagerTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target"));
private NodeState root = INITIAL_CONTENT;
private NodeBuilder builder = root.builder();
private IndexCopier indexCopier;
private NRTIndexFactory nrtFactory;
private LuceneIndexReaderFactory readerFactory;
@Before
public void setUp() throws IOException {
indexCopier = new IndexCopier(sameThreadExecutor(), temporaryFolder.getRoot());
nrtFactory = new NRTIndexFactory(indexCopier, StatisticsProvider.NOOP);
readerFactory = new DefaultIndexReaderFactory(Mounts.defaultMountInfoProvider(), indexCopier);
LuceneIndexEditorContext.configureUniqueId(builder);
}
@After
public void cleanup() throws IOException {
nrtFactory.close();
indexCopier.close();
}
@Test
public void nullIndexNode() throws Exception{
assertNull(LuceneIndexNodeManager.open("/foo", root, builder.getNodeState(), readerFactory, null));
assertNull(LuceneIndexNodeManager.open("/foo", root, builder.getNodeState(), readerFactory, nrtFactory));
}
@Test
public void nonNullIndex_OnlyNRT() throws Exception{
LuceneIndexNodeManager nodeManager = LuceneIndexNodeManager.open("/foo", root, createNRTIndex(), readerFactory, nrtFactory);
LuceneIndexNode node = nodeManager.acquire();
assertNotNull(node.getSearcher());
TopDocs docs = node.getSearcher().search(new TermQuery(new Term(PATH, "/content/en")), 100);
assertEquals(0, docs.totalHits);
node.release();
node.getLocalWriter().updateDocument("/content/en", newDoc("/content/en"));
node.refreshReadersOnWriteIfRequired();
node = nodeManager.acquire();
docs = node.getSearcher().search(new TermQuery(new Term(PATH, "/content/en")), 100);
assertEquals(1, docs.totalHits);
}
@Test
public void nullIndex_NonFreshIndex() throws Exception{
NodeBuilder builder = createNRTIndex().builder();
NodeBuilder rootBuilder = root.builder();
rootBuilder.child(IndexNodeManager.ASYNC).setProperty("async", "async");
assertNull(LuceneIndexNodeManager.open("/foo", rootBuilder.getNodeState(), builder.getNodeState(), readerFactory, nrtFactory));
}
@Test
public void lockAndRefreshPolicy() throws Exception {
NodeState state = createNRTIndex();
LuceneIndexDefinition definition = new LuceneIndexDefinition(root, state, "/foo");
NRTIndex nrtIndex = nrtFactory.createIndex(definition);
NRTIndex mock = spy(nrtIndex);
doReturn(new FailingPolicy()).when(mock).getRefreshPolicy();
LuceneIndexNodeManager node = new LuceneIndexNodeManager("/foo", definition, Collections.emptyList(), mock);
try {
node.acquire();
fail();
} catch (Exception ignore) {
}
node.close();
}
@Test
public void indexOpenedBeforeFistCycle() throws Exception {
NodeState nrtIndex = createNRTIndex();
NodeState asyncIndex = nrtIndex.builder().setProperty("async", ImmutableList.of("async"), STRINGS).getNodeState();
NodeState nonAsyncIndex;
{
NodeBuilder builder = nrtIndex.builder();
builder.removeProperty("async");
nonAsyncIndex = builder.getNodeState();
}
assertNotNull("nrtIndex; Non existing /:async",
LuceneIndexNodeManager.open("/foo", root, nrtIndex, readerFactory, nrtFactory));
assertNull("asyncIndex; Non existing /:async",
LuceneIndexNodeManager.open("/foo", root, asyncIndex, readerFactory, nrtFactory));
assertNull("nonAsyncIndex; Non existing /:async",
LuceneIndexNodeManager.open("/foo", root, nonAsyncIndex, readerFactory, nrtFactory));
// Fake an empty /:async - first indexing cycle isn't done yet
builder.child(":async");
root = builder.getNodeState();
assertNotNull("nrtIndex; empty /:async",
LuceneIndexNodeManager.open("/foo", root, nrtIndex, readerFactory, nrtFactory));
assertNull("asyncIndex; empty /:async",
LuceneIndexNodeManager.open("/foo", root, asyncIndex, readerFactory, nrtFactory));
assertNull("nonAsyncIndex; empty /:async",
LuceneIndexNodeManager.open("/foo", root, nonAsyncIndex, readerFactory, nrtFactory));
// Fake async indexing cycle done with no data
builder.child(":async").setProperty("async", "some-random-id");
root = builder.getNodeState();
assertNull("nrtIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, nrtIndex, readerFactory, nrtFactory));
assertNull("asyncIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, asyncIndex, readerFactory, nrtFactory));
assertNull("nonAsyncIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, nonAsyncIndex, readerFactory, nrtFactory));
}
@Test
public void indexWithIndexedDataOpenedBeforeFistCycle() throws Exception {
NodeState nrtIndex = createNRTIndex();
{
NodeBuilder indexBuilder = nrtIndex.builder();
LuceneIndexDefinition indexDefinition = new LuceneIndexDefinition(root, indexBuilder.getNodeState(), "/foo");
IndexWriterConfig config = IndexWriterUtils.getIndexWriterConfig(indexDefinition, false);
OakDirectory directory = new OakDirectory(indexBuilder, indexDefinition, false);
IndexWriter writer = new IndexWriter(directory, config);
writer.addDocument(newDoc("/content/en").getFields());
writer.close();
directory.close();
nrtIndex = indexBuilder.getNodeState();
}
NodeState asyncIndex = nrtIndex.builder().setProperty("async", ImmutableList.of("async"), STRINGS).getNodeState();
NodeState nonAsyncIndex;
{
NodeBuilder builder = nrtIndex.builder();
builder.removeProperty("async");
nonAsyncIndex = builder.getNodeState();
}
// absent or empty /:async doesn't make sense with already indexed data available.
// So, we're considering only this case.
// Fake async indexing cycle done with no data
builder.child(":async").setProperty("async", "some-random-id");
root = builder.getNodeState();
assertNotNull("nrtIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, nrtIndex, readerFactory, nrtFactory));
assertNotNull("asyncIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, asyncIndex, readerFactory, nrtFactory));
assertNotNull("nonAsyncIndex; fake async cycle run",
LuceneIndexNodeManager.open("/foo", root, nonAsyncIndex, readerFactory, nrtFactory));
}
@Test
public void hasIndexingRun() {
NodeState nrtIndex = createNRTIndex();
NodeState asyncIndex = nrtIndex.builder().setProperty("async", ImmutableList.of("async"), STRINGS).getNodeState();
NodeState nonAsyncIndex;
{
NodeBuilder builder = nrtIndex.builder();
builder.removeProperty("async");
nonAsyncIndex = builder.getNodeState();
}
assertFalse("nrtIndex; Non existing /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nrtIndex));
assertFalse("asyncIndex; Non existing /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", asyncIndex));
assertFalse("nonAsyncIndex; Non existing /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nonAsyncIndex));
// Fake an empty /:async - first indexing cycle isn't done yet
builder.child(":async");
root = builder.getNodeState();
assertFalse("nrtIndex; Empty /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nrtIndex));
assertFalse("asyncIndex; Non existing /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", asyncIndex));
assertFalse("nonAsyncIndex; Non existing /:async",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nonAsyncIndex));
// Fake async indexing cycle done
builder.child(":async").setProperty("async", "some-random-id");
root = builder.getNodeState();
assertTrue("nrtIndex; fake async cycle run",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nrtIndex));
assertTrue("asyncIndex; fake async cycle run",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", asyncIndex));
assertFalse("nonAsyncIndex; fake async cycle run",
LuceneIndexNodeManager.hasAsyncIndexerRun(root, "/foo", nonAsyncIndex));
}
private static NodeState createNRTIndex(){
IndexDefinitionBuilder idx = new IndexDefinitionBuilder();
idx.indexRule("nt:base").property("foo").propertyIndex();
idx.async("async", "sync");
return idx.build();
}
private static class FailingPolicy implements ReaderRefreshPolicy {
@Override
public void refreshOnReadIfRequired(Runnable refreshCallback) {
throw new IllegalStateException();
}
@Override
public void refreshOnWriteIfRequired(Runnable refreshCallback) {
throw new IllegalStateException();
}
}
}