Index: lucene/src/test/org/apache/lucene/search/TestSearcherManager.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestSearcherManager.java (revision 1213001) +++ lucene/src/test/org/apache/lucene/search/TestSearcherManager.java (working copy) @@ -29,14 +29,17 @@ import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.ConcurrentMergeScheduler; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase; +import org.apache.lucene.search.SearcherManager.SearcherFactory; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase.UseNoMemoryExpensiveCodec; import org.apache.lucene.util.NamedThreadFactory; import org.apache.lucene.util._TestUtil; +import org.junit.Ignore; @UseNoMemoryExpensiveCodec public class TestSearcherManager extends ThreadedIndexingAndSearchingTestCase { @@ -65,24 +68,26 @@ private boolean isNRT; @Override - protected void doAfterWriter(ExecutorService es) throws Exception { - final SearcherWarmer warmer = new SearcherWarmer() { + protected void doAfterWriter(final ExecutorService es) throws Exception { + final SearcherFactory factory = new SearcherFactory() { @Override - public void warm(IndexSearcher s) throws IOException { + public IndexSearcher newSearcher(IndexReader r) throws IOException { + IndexSearcher s = new IndexSearcher(r, es); TestSearcherManager.this.warmCalled = true; s.search(new TermQuery(new Term("body", "united")), 10); + return s; } }; if (random.nextBoolean()) { // TODO: can we randomize the applyAllDeletes? But // somehow for final searcher we must apply // deletes... - mgr = new SearcherManager(writer, true, warmer, es); + mgr = new SearcherManager(writer, true, factory); isNRT = true; } else { // SearcherManager needs to see empty commit: writer.commit(); - mgr = new SearcherManager(dir, warmer, es); + mgr = new SearcherManager(dir, factory); isNRT = false; } @@ -191,20 +196,26 @@ writer.commit(); final CountDownLatch awaitEnterWarm = new CountDownLatch(1); final CountDownLatch awaitClose = new CountDownLatch(1); + final AtomicBoolean triedReopen = new AtomicBoolean(false); final ExecutorService es = random.nextBoolean() ? null : Executors.newCachedThreadPool(new NamedThreadFactory("testIntermediateClose")); - final SearcherWarmer warmer = new SearcherWarmer() { + final SearcherFactory factory = new SearcherFactory() { @Override - public void warm(IndexSearcher s) throws IOException { + public IndexSearcher newSearcher(IndexReader r) throws IOException { try { - awaitEnterWarm.countDown(); - awaitClose.await(); + if (triedReopen.get()) { + awaitEnterWarm.countDown(); + awaitClose.await(); + } } catch (InterruptedException e) { // } + return new IndexSearcher(r, es); } }; - final SearcherManager searcherManager = random.nextBoolean() ? new SearcherManager(dir, - warmer, es) : new SearcherManager(writer, random.nextBoolean(), warmer, es); + final SearcherManager searcherManager = random.nextBoolean() + ? new SearcherManager(dir, factory) + : new SearcherManager(writer, random.nextBoolean(), factory); + System.out.println("sm created"); IndexSearcher searcher = searcherManager.acquire(); try { assertEquals(1, searcher.getIndexReader().numDocs()); @@ -214,13 +225,13 @@ writer.addDocument(new Document()); writer.commit(); final AtomicBoolean success = new AtomicBoolean(false); - final AtomicBoolean triedReopen = new AtomicBoolean(false); final Throwable[] exc = new Throwable[1]; Thread thread = new Thread(new Runnable() { @Override public void run() { try { triedReopen.set(true); + System.out.println("NOW call maybeReopen"); searcherManager.maybeReopen(); success.set(true); } catch (AlreadyClosedException e) { @@ -236,7 +247,9 @@ } }); thread.start(); + System.out.println("THREAD started"); awaitEnterWarm.await(); + System.out.println("NOW call close"); searcherManager.close(); awaitClose.countDown(); thread.join(); Index: lucene/src/test/org/apache/lucene/search/TestNRTManager.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestNRTManager.java (revision 1213001) +++ lucene/src/test/org/apache/lucene/search/TestNRTManager.java (working copy) @@ -28,12 +28,14 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.TextField; import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.Term; import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase; import org.apache.lucene.search.NRTManagerReopenThread; +import org.apache.lucene.search.SearcherManager.SearcherFactory; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.NRTCachingDirectory; @@ -190,7 +192,7 @@ private NRTManager nrt; private NRTManagerReopenThread nrtThread; @Override - protected void doAfterWriter(ExecutorService es) throws Exception { + protected void doAfterWriter(final ExecutorService es) throws Exception { final double minReopenSec = 0.01 + 0.05 * random.nextDouble(); final double maxReopenSec = minReopenSec * (1.0 + 10 * random.nextDouble()); @@ -198,14 +200,16 @@ System.out.println("TEST: make NRTManager maxReopenSec=" + maxReopenSec + " minReopenSec=" + minReopenSec); } - nrt = new NRTManager(writer, es, - new SearcherWarmer() { - @Override - public void warm(IndexSearcher s) throws IOException { - TestNRTManager.this.warmCalled = true; - s.search(new TermQuery(new Term("body", "united")), 10); - } - }, false); + nrt = new NRTManager(writer, + new SearcherFactory() { + @Override + public IndexSearcher newSearcher(IndexReader r) throws IOException { + TestNRTManager.this.warmCalled = true; + IndexSearcher s = new IndexSearcher(r, es); + s.search(new TermQuery(new Term("body", "united")), 10); + return s; + } + }, false); nrtThread = new NRTManagerReopenThread(nrt, maxReopenSec, minReopenSec); nrtThread.setName("NRT Reopen Thread"); @@ -267,7 +271,7 @@ final CountDownLatch signal = new CountDownLatch(1); LatchedIndexWriter writer = new LatchedIndexWriter(d, conf, latch, signal); - final NRTManager manager = new NRTManager(writer, null, null, false); + final NRTManager manager = new NRTManager(writer, null, false); Document doc = new Document(); doc.add(newField("test","test", TextField.TYPE_STORED)); long gen = manager.addDocument(doc); Index: lucene/src/java/org/apache/lucene/search/SearcherWarmer.java =================================================================== --- lucene/src/java/org/apache/lucene/search/SearcherWarmer.java (revision 1213001) +++ lucene/src/java/org/apache/lucene/search/SearcherWarmer.java (working copy) @@ -1,34 +0,0 @@ -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 org.apache.lucene.search.NRTManager; // javadocs - -/** Pass an implementation of this to {@link NRTManager} or - * {@link SearcherManager} to warm a new {@link - * IndexSearcher} before it's put into production. - * - * @lucene.experimental */ - -public interface SearcherWarmer { - // TODO: can we somehow merge this w/ IW's - // IndexReaderWarmer.... should IW switch to this? - public void warm(IndexSearcher s) throws IOException; -} Index: lucene/src/java/org/apache/lucene/search/SearcherManager.java =================================================================== --- lucene/src/java/org/apache/lucene/search/SearcherManager.java (revision 1213001) +++ lucene/src/java/org/apache/lucene/search/SearcherManager.java (working copy) @@ -18,7 +18,6 @@ */ import java.io.IOException; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; import org.apache.lucene.index.CorruptIndexException; @@ -67,8 +66,7 @@ public final class SearcherManager { private volatile IndexSearcher currentSearcher; - private final ExecutorService es; - private final SearcherWarmer warmer; + private final SearcherFactory searcherFactory; private final Semaphore reopenLock = new Semaphore(1); /** @@ -81,54 +79,35 @@ * 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. + * @param searcherFactory An optional {@link SearcherFactory}. Pass + * null if you don't require the searcher to be warmed + * before going live or other custom behavior. * * @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)); - } - }); + public SearcherManager(IndexWriter writer, boolean applyAllDeletes, SearcherFactory searcherFactory) throws IOException { + if (searcherFactory == null) { + searcherFactory = new SearcherFactory(); } + this.searcherFactory = searcherFactory; + currentSearcher = searcherFactory.newSearcher(IndexReader.open(writer, applyAllDeletes)); } /** * 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. + * @param searcherFactory An optional {@link SearcherFactory}. Pass + * null if you don't require the searcher to be warmed + * before going live or other custom behavior. * * @throws IOException */ - public SearcherManager(Directory dir, SearcherWarmer warmer, - ExecutorService es) throws IOException { - this.es = es; - this.warmer = warmer; - currentSearcher = new IndexSearcher(IndexReader.open(dir), es); + public SearcherManager(Directory dir, SearcherFactory searcherFactory) throws IOException { + if (searcherFactory == null) { + searcherFactory = new SearcherFactory(); + } + this.searcherFactory = searcherFactory; + currentSearcher = searcherFactory.newSearcher(IndexReader.open(dir)); } /** @@ -167,12 +146,9 @@ release(searcherToReopen); } if (newReader != null) { - final IndexSearcher newSearcher = new IndexSearcher(newReader, es); + final IndexSearcher newSearcher = searcherFactory.newSearcher(newReader); boolean success = false; try { - if (warmer != null) { - warmer.warm(newSearcher); - } swapSearcher(newSearcher); success = true; } finally { @@ -260,5 +236,10 @@ currentSearcher = newSearcher; release(oldSearcher); } - + + public static class SearcherFactory { + public IndexSearcher newSearcher(IndexReader r) throws IOException { + return new IndexSearcher(r); + } + } } Index: lucene/src/java/org/apache/lucene/search/NRTManager.java =================================================================== --- lucene/src/java/org/apache/lucene/search/NRTManager.java (revision 1213001) +++ lucene/src/java/org/apache/lucene/search/NRTManager.java (working copy) @@ -21,7 +21,6 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; @@ -34,6 +33,7 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; // javadocs +import org.apache.lucene.search.SearcherManager.SearcherFactory; import org.apache.lucene.store.Directory; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.ThreadInterruptedException; @@ -65,60 +65,30 @@ /** * Create new NRTManager. * - * @param writer IndexWriter to open near-real-time - * readers - * @param warmer 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. - * - *

NOTE: the provided {@link SearcherWarmer} is - * not invoked for the initial searcher; you should - * warm it yourself if necessary. + * @param writer IndexWriter to open near-real-time + * readers + * @param searcherFactory An optional {@link SearcherFactory}. Pass + * null if you don't require the searcher to be warmed + * before going live or other custom behavior. */ - public NRTManager(IndexWriter writer, SearcherWarmer warmer) throws IOException { - this(writer, null, warmer, true); + public NRTManager(IndexWriter writer, SearcherFactory searcherFactory) throws IOException { + this(writer, searcherFactory, true); } /** - * Create new NRTManager. - * - * @param writer IndexWriter to open near-real-time - * readers - * @param es optional ExecutorService so different segments can - * be searched concurrently (see {@link IndexSearcher#IndexSearcher(IndexReader, ExecutorService)}. - * Pass null to search segments sequentially. - * @param warmer 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. - * - *

NOTE: the provided {@link SearcherWarmer} is - * not invoked for the initial searcher; you should - * warm it yourself if necessary. - */ - public NRTManager(IndexWriter writer, ExecutorService es, - SearcherWarmer warmer) throws IOException { - this(writer, es, warmer, true); - } - - /** * Expert: just like {@link - * #NRTManager(IndexWriter,ExecutorService,SearcherWarmer)}, + * #NRTManager(IndexWriter,SearcherWarmer)}, * but you can also specify whether every searcher must * apply deletes. This is useful for cases where certain * uses can tolerate seeing some deleted docs, since * reopen time is faster if deletes need not be applied. */ - public NRTManager(IndexWriter writer, ExecutorService es, - SearcherWarmer warmer, boolean alwaysApplyDeletes) throws IOException { + public NRTManager(IndexWriter writer, SearcherFactory searcherFactory, boolean alwaysApplyDeletes) throws IOException { this.writer = writer; if (alwaysApplyDeletes) { - withoutDeletes = withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, warmer, es)); + withoutDeletes = withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, searcherFactory)); } else { - withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, warmer, es)); - withoutDeletes = new SearcherManagerRef(false, 0, new SearcherManager(writer, false, warmer, es)); + withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, searcherFactory)); + withoutDeletes = new SearcherManagerRef(false, 0, new SearcherManager(writer, false, searcherFactory)); } indexingGen = new AtomicLong(1); }