blob: 82fd2dd9dabbcbd185b0461a839748116a20c31b [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.lucene.facet.taxonomy;
import java.io.IOException;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ReferenceManager;
import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.IOUtils;
/**
* Manages near-real-time reopen of both an IndexSearcher
* and a TaxonomyReader.
*
* <p><b>NOTE</b>: If you call {@link
* DirectoryTaxonomyWriter#replaceTaxonomy} then you must
* open a new {@code SearcherTaxonomyManager} afterwards.
*/
public class SearcherTaxonomyManager extends ReferenceManager<SearcherTaxonomyManager.SearcherAndTaxonomy> {
/** Holds a matched pair of {@link IndexSearcher} and
* {@link TaxonomyReader} */
public static class SearcherAndTaxonomy {
/** Point-in-time {@link IndexSearcher}. */
public final IndexSearcher searcher;
/** Matching point-in-time {@link DirectoryTaxonomyReader}. */
public final DirectoryTaxonomyReader taxonomyReader;
/** Create a SearcherAndTaxonomy */
public SearcherAndTaxonomy(IndexSearcher searcher, DirectoryTaxonomyReader taxonomyReader) {
this.searcher = searcher;
this.taxonomyReader = taxonomyReader;
}
}
private final SearcherFactory searcherFactory;
private final long taxoEpoch;
private final DirectoryTaxonomyWriter taxoWriter;
/** Creates near-real-time searcher and taxonomy reader
* from the corresponding writers. */
public SearcherTaxonomyManager(IndexWriter writer, SearcherFactory searcherFactory, DirectoryTaxonomyWriter taxoWriter) throws IOException {
this(writer, true, searcherFactory, taxoWriter);
}
/** Expert: creates near-real-time searcher and taxonomy reader
* from the corresponding writers, controlling whether deletes should be applied. */
public SearcherTaxonomyManager(IndexWriter writer, boolean applyAllDeletes, SearcherFactory searcherFactory, DirectoryTaxonomyWriter taxoWriter) throws IOException {
if (searcherFactory == null) {
searcherFactory = new SearcherFactory();
}
this.searcherFactory = searcherFactory;
this.taxoWriter = taxoWriter;
DirectoryTaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoWriter);
current = new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, DirectoryReader.open(writer, applyAllDeletes, false), null), taxoReader);
this.taxoEpoch = taxoWriter.getTaxonomyEpoch();
}
/**
* Creates search and taxonomy readers over the corresponding directories.
*
* <p>
* <b>NOTE:</b> you should only use this constructor if you commit and call
* {@link #maybeRefresh()} in the same thread. Otherwise it could lead to an
* unsync'd {@link IndexSearcher} and {@link TaxonomyReader} pair.
*/
public SearcherTaxonomyManager(Directory indexDir, Directory taxoDir, SearcherFactory searcherFactory) throws IOException {
if (searcherFactory == null) {
searcherFactory = new SearcherFactory();
}
this.searcherFactory = searcherFactory;
DirectoryTaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
current = new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, DirectoryReader.open(indexDir), null), taxoReader);
this.taxoWriter = null;
taxoEpoch = -1;
}
/**
* Creates this from already opened {@link IndexReader} and {@link DirectoryTaxonomyReader} instances. Note that
* the incoming readers will be closed when you call {@link #close}.
*/
public SearcherTaxonomyManager(IndexReader reader, DirectoryTaxonomyReader taxoReader, SearcherFactory searcherFactory) throws IOException {
if (searcherFactory == null) {
searcherFactory = new SearcherFactory();
}
this.searcherFactory = searcherFactory;
current = new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, reader, null), taxoReader);
this.taxoWriter = null;
taxoEpoch = -1;
}
@Override
protected void decRef(SearcherAndTaxonomy ref) throws IOException {
ref.searcher.getIndexReader().decRef();
// This decRef can fail, and then in theory we should
// tryIncRef the searcher to put back the ref count
// ... but 1) the below decRef should only fail because
// it decRef'd to 0 and closed and hit some IOException
// during close, in which case 2) very likely the
// searcher was also just closed by the above decRef and
// a tryIncRef would fail:
ref.taxonomyReader.decRef();
}
@Override
protected boolean tryIncRef(SearcherAndTaxonomy ref) throws IOException {
if (ref.searcher.getIndexReader().tryIncRef()) {
if (ref.taxonomyReader.tryIncRef()) {
return true;
} else {
ref.searcher.getIndexReader().decRef();
}
}
return false;
}
@Override
protected SearcherAndTaxonomy refreshIfNeeded(SearcherAndTaxonomy ref) throws IOException {
// Must re-open searcher first, otherwise we may get a
// new reader that references ords not yet known to the
// taxonomy reader:
final IndexReader r = ref.searcher.getIndexReader();
final IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) r);
if (newReader == null) {
return null;
} else {
DirectoryTaxonomyReader tr;
try {
tr = TaxonomyReader.openIfChanged(ref.taxonomyReader);
} catch (Throwable t1) {
try {
IOUtils.close(newReader);
} catch (Throwable t2) {
t2.addSuppressed(t2);
}
throw t1;
}
if (tr == null) {
ref.taxonomyReader.incRef();
tr = ref.taxonomyReader;
} else if (taxoWriter != null && taxoWriter.getTaxonomyEpoch() != taxoEpoch) {
IOUtils.close(newReader, tr);
throw new IllegalStateException("DirectoryTaxonomyWriter.replaceTaxonomy was called, which is not allowed when using SearcherTaxonomyManager");
}
return new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, newReader, r), tr);
}
}
@Override
protected int getRefCount(SearcherAndTaxonomy reference) {
return reference.searcher.getIndexReader().getRefCount();
}
}