X-Git-Url: https://git.mdrn.pl/pylucene.git/blobdiff_plain/a2e61f0c04805cfcb8706176758d1283c7e3a55c..aaeed5504b982cf3545252ab528713250aa33eed:/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/SearcherManager.java diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/SearcherManager.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/SearcherManager.java new file mode 100644 index 0000000..3a0588b --- /dev/null +++ b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/SearcherManager.java @@ -0,0 +1,254 @@ +package org.apache.lucene.search; + +/** + * 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. + */ + +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; + +import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.NRTManager; // javadocs +import org.apache.lucene.search.IndexSearcher; // javadocs +import org.apache.lucene.store.AlreadyClosedException; +import org.apache.lucene.store.Directory; + +/** + * Utility class to safely share {@link IndexSearcher} instances across multiple + * threads, while periodically reopening. This class ensures each searcher is + * closed only once all threads have finished using it. + * + *
+ * Use {@link #acquire} to obtain the current searcher, and {@link #release} to + * release it, like this: + * + *
+ * IndexSearcher s = manager.acquire(); + * try { + * // Do searching, doc retrieval, etc. with s + * } finally { + * manager.release(s); + * } + * // Do not use s after this! + * s = null; + *+ * + *
+ * In addition you should periodically call {@link #maybeReopen}. While it's + * possible to call this just before running each query, this is discouraged + * since it penalizes the unlucky queries that do the reopen. It's better to use + * a separate background thread, that periodically calls maybeReopen. Finally, + * be sure to call {@link #close} once you are done. + * + *
+ * NOTE: if you have an {@link IndexWriter}, it's better to use
+ * {@link NRTManager} since that class pulls near-real-time readers from the
+ * IndexWriter.
+ *
+ * @lucene.experimental
+ */
+
+public final class SearcherManager {
+
+ private volatile IndexSearcher currentSearcher;
+ private final ExecutorService es;
+ private final SearcherWarmer warmer;
+ private final Semaphore reopenLock = new Semaphore(1);
+
+ /**
+ * Creates and returns a new SearcherManager from the given {@link IndexWriter}.
+ * @param writer the IndexWriter to open the IndexReader from.
+ * @param applyAllDeletes If true
, all buffered deletes will
+ * be applied (made visible) in the {@link IndexSearcher} / {@link IndexReader}.
+ * If false
, the deletes may or may not be applied, but remain buffered
+ * (in IndexWriter) so that they will be applied in the future.
+ * Applying deletes can be costly, so if your app can tolerate deleted documents
+ * being returned you might gain some performance by passing false
.
+ * See {@link IndexReader#openIfChanged(IndexReader, IndexWriter, boolean)}.
+ * @param warmer An optional {@link SearcherWarmer}. Pass
+ * null
if you don't require the searcher to warmed
+ * before going live. If this is non-null
then a
+ * merged segment warmer is installed on the
+ * provided IndexWriter's config.
+ * @param es An optional {@link ExecutorService} so different segments can
+ * be searched concurrently (see {@link
+ * IndexSearcher#IndexSearcher(IndexReader,ExecutorService)}. Pass null
+ * to search segments sequentially.
+ *
+ * @throws IOException
+ */
+ public SearcherManager(IndexWriter writer, boolean applyAllDeletes,
+ final SearcherWarmer warmer, final ExecutorService es) throws IOException {
+ this.es = es;
+ this.warmer = warmer;
+ currentSearcher = new IndexSearcher(IndexReader.open(writer, applyAllDeletes));
+ if (warmer != null) {
+ writer.getConfig().setMergedSegmentWarmer(
+ new IndexWriter.IndexReaderWarmer() {
+ @Override
+ public void warm(IndexReader reader) throws IOException {
+ warmer.warm(new IndexSearcher(reader, es));
+ }
+ });
+ }
+ }
+
+ /**
+ * Creates and returns a new SearcherManager from the given {@link Directory}.
+ * @param dir the directory to open the IndexReader on.
+ * @param warmer An optional {@link SearcherWarmer}. Pass
+ * null
if you don't require the searcher to warmed
+ * before going live. If this is non-null
then a
+ * merged segment warmer is installed on the
+ * provided IndexWriter's config.
+ * @param es And optional {@link ExecutorService} so different segments can
+ * be searched concurrently (see {@link
+ * IndexSearcher#IndexSearcher(IndexReader,ExecutorService)}. Pass null
+ * to search segments sequentially.
+ *
+ * @throws IOException
+ */
+ public SearcherManager(Directory dir, SearcherWarmer warmer,
+ ExecutorService es) throws IOException {
+ this.es = es;
+ this.warmer = warmer;
+ currentSearcher = new IndexSearcher(IndexReader.open(dir, true), es);
+ }
+
+ /**
+ * You must call this, periodically, to perform a reopen. This calls
+ * {@link IndexReader#openIfChanged(IndexReader)} with the underlying reader, and if that returns a
+ * new reader, it's warmed (if you provided a {@link SearcherWarmer} and then
+ * swapped into production.
+ *
+ *
+ * Threads: it's fine for more than one thread to call this at once. + * Only the first thread will attempt the reopen; subsequent threads will see + * that another thread is already handling reopen and will return immediately. + * Note that this means if another thread is already reopening then subsequent + * threads will return right away without waiting for the reader reopen to + * complete. + *
+ * + *+ * This method returns true if a new reader was in fact opened or + * if the current searcher has no pending changes. + *
+ */ + public boolean maybeReopen() throws IOException { + ensureOpen(); + // Ensure only 1 thread does reopen at once; other + // threads just return immediately: + if (reopenLock.tryAcquire()) { + try { + // IR.openIfChanged preserves NRT and applyDeletes + // in the newly returned reader: + final IndexReader newReader = IndexReader.openIfChanged(currentSearcher.getIndexReader()); + if (newReader != null) { + final IndexSearcher newSearcher = new IndexSearcher(newReader, es); + boolean success = false; + try { + if (warmer != null) { + warmer.warm(newSearcher); + } + swapSearcher(newSearcher); + success = true; + } finally { + if (!success) { + release(newSearcher); + } + } + } + return true; + } finally { + reopenLock.release(); + } + } else { + return false; + } + } + + /** + * Returnstrue
if no changes have occured since this searcher
+ * ie. reader was opened, otherwise false
.
+ * @see IndexReader#isCurrent()
+ */
+ public boolean isSearcherCurrent() throws CorruptIndexException,
+ IOException {
+ final IndexSearcher searcher = acquire();
+ try {
+ return searcher.getIndexReader().isCurrent();
+ } finally {
+ release(searcher);
+ }
+ }
+
+ /**
+ * Release the searcher previously obtained with {@link #acquire}.
+ *
+ * + * NOTE: it's safe to call this after {@link #close}. + */ + public void release(IndexSearcher searcher) throws IOException { + assert searcher != null; + searcher.getIndexReader().decRef(); + } + + /** + * Close this SearcherManager to future searching. Any searches still in + * process in other threads won't be affected, and they should still call + * {@link #release} after they are done. + */ + public synchronized void close() throws IOException { + if (currentSearcher != null) { + // make sure we can call this more than once + // closeable javadoc says: + // if this is already closed then invoking this method has no effect. + swapSearcher(null); + } + } + + /** + * Obtain the current IndexSearcher. You must match every call to acquire with + * one call to {@link #release}; it's best to do so in a finally clause. + */ + public IndexSearcher acquire() { + IndexSearcher searcher; + do { + if ((searcher = currentSearcher) == null) { + throw new AlreadyClosedException("this SearcherManager is closed"); + } + } while (!searcher.getIndexReader().tryIncRef()); + return searcher; + } + + private void ensureOpen() { + if (currentSearcher == null) { + throw new AlreadyClosedException("this SearcherManager is closed"); + } + } + + private synchronized void swapSearcher(IndexSearcher newSearcher) throws IOException { + ensureOpen(); + final IndexSearcher oldSearcher = currentSearcher; + currentSearcher = newSearcher; + release(oldSearcher); + } + +}