You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2013/04/10 18:25:23 UTC

svn commit: r1466548 - in /lucene/dev/trunk/lucene: ./ core/src/java/org/apache/lucene/search/ facet/src/java/org/apache/lucene/facet/search/ facet/src/java/org/apache/lucene/facet/taxonomy/ facet/src/java/org/apache/lucene/facet/taxonomy/directory/ fa...

Author: mikemccand
Date: Wed Apr 10 16:25:22 2013
New Revision: 1466548

URL: http://svn.apache.org/r1466548
Log:
LUCENE-3786: add SearcherTaxonomyManager

Added:
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java   (with props)
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java   (with props)
Modified:
    lucene/dev/trunk/lucene/CHANGES.txt
    lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java
    lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java
    lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java

Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Wed Apr 10 16:25:22 2013
@@ -179,6 +179,10 @@ New Features
 
 * LUCENE-4904: Added descending sort order to NumericDocValuesSorter. (Shai Erera)
 
+* LUCENE-3786: Added SearcherTaxonomyManager, to manage access to both
+  IndexSearcher and DirectoryTaxonomyReader for near-real-time
+  faceting.  (Shai Erera, Mike McCandless)
+
 Optimizations
 
 * LUCENE-4839: SorterTemplate.merge can now be overridden in order to replace

Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java Wed Apr 10 16:25:22 2013
@@ -81,7 +81,7 @@ public abstract class ReferenceManager<G
    * the operation was successful.
    * @throws AlreadyClosedException if the reference manager has been {@link #close() closed}. 
    */
-  protected abstract boolean tryIncRef(G reference);
+  protected abstract boolean tryIncRef(G reference) throws IOException;
 
   /**
    * Obtain the current reference. You must match every call to acquire with one
@@ -90,7 +90,7 @@ public abstract class ReferenceManager<G
    * released.
    * @throws AlreadyClosedException if the reference manager has been {@link #close() closed}. 
    */
-  public final G acquire() {
+  public final G acquire() throws IOException {
     G ref;
     do {
       if ((ref = current) == null) {

Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java Wed Apr 10 16:25:22 2013
@@ -144,8 +144,11 @@ public final class SearcherManager exten
     }
   }
 
-  // NOTE: decRefs incoming reader on throwing an exception
-  static IndexSearcher getSearcher(SearcherFactory searcherFactory, IndexReader reader) throws IOException {
+  /** Expert: creates a searcher from the provided {@link
+   *  IndexReader} using the provided {@link
+   *  SearcherFactory}.  NOTE: this decRefs incoming reader
+   * on throwing an exception. */
+  public static IndexSearcher getSearcher(SearcherFactory searcherFactory, IndexReader reader) throws IOException {
     boolean success = false;
     final IndexSearcher searcher;
     try {

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java Wed Apr 10 16:25:22 2013
@@ -83,6 +83,7 @@ public final class FastCountingFacetsAgg
           byte b = buf.bytes[offset++];
           if (b >= 0) {
             prev = ord = ((ord << 7) | b) + prev;
+            assert ord < counts.length: "ord=" + ord + " vs maxOrd=" + counts.length;
             ++counts[ord];
             ord = 0;
           } else {

Added: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java?rev=1466548&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java (added)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java Wed Apr 10 16:25:22 2013
@@ -0,0 +1,122 @@
+package org.apache.lucene.facet.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.facet.taxonomy.TaxonomyReader;
+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.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 {
+    public final IndexSearcher searcher;
+    public final DirectoryTaxonomyReader taxonomyReader;
+
+    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, 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)),
+                                      taxoReader);
+    taxoEpoch = taxoWriter.getTaxonomyEpoch();
+  }
+
+  @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 = TaxonomyReader.openIfChanged(ref.taxonomyReader);
+      if (tr == null) {
+        ref.taxonomyReader.incRef();
+        tr = ref.taxonomyReader;
+      } else if (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), tr);
+    }
+  }
+}

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java Wed Apr 10 16:25:22 2013
@@ -246,4 +246,16 @@ public abstract class TaxonomyReader imp
     refCount.incrementAndGet();
   }
 
+  /** Expert: increments the refCount of this TaxonomyReader
+   *  instance only if it has not been closed yet.  Returns
+   *  true on success. */
+  public final boolean tryIncRef() {
+    int count;
+    while ((count = refCount.get()) > 0) {
+      if (refCount.compareAndSet(count, count+1)) {
+        return true;
+      }
+    }
+    return false;
+  }
 }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java Wed Apr 10 16:25:22 2013
@@ -9,7 +9,7 @@ import org.apache.lucene.facet.collectio
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.ParallelTaxonomyArrays;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
-import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.CorruptIndexException; // javadocs
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexWriter;

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java Wed Apr 10 16:25:22 2013
@@ -29,7 +29,7 @@ import org.apache.lucene.facet.taxonomy.
 import org.apache.lucene.facet.taxonomy.writercache.lru.LruTaxonomyWriterCache;
 import org.apache.lucene.index.AtomicReader;
 import org.apache.lucene.index.AtomicReaderContext;
-import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.CorruptIndexException; // javadocs
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
@@ -44,7 +44,7 @@ import org.apache.lucene.index.TermsEnum
 import org.apache.lucene.index.TieredMergePolicy;
 import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.LockObtainFailedException;
+import org.apache.lucene.store.LockObtainFailedException; // javadocs
 import org.apache.lucene.store.NativeFSLockFactory;
 import org.apache.lucene.store.SimpleFSLockFactory;
 import org.apache.lucene.util.BytesRef;
@@ -991,9 +991,12 @@ public class DirectoryTaxonomyWriter imp
     return indexWriter;
   }
   
-  /** Used by {@link DirectoryTaxonomyReader} to support NRT. */
-  final long getTaxonomyEpoch() {
+  /** Expert: returns current index epoch, if this is a
+   * near-real-time reader.  Used by {@link
+   * DirectoryTaxonomyReader} to support NRT. 
+   *
+   * @lucene.internal */
+  public final long getTaxonomyEpoch() {
     return indexEpoch;
   }
-  
 }

Added: lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java?rev=1466548&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java (added)
+++ lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java Wed Apr 10 16:25:22 2013
@@ -0,0 +1,189 @@
+package org.apache.lucene.facet.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.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.facet.index.FacetFields;
+import org.apache.lucene.facet.params.FacetIndexingParams;
+import org.apache.lucene.facet.params.FacetSearchParams;
+import org.apache.lucene.facet.search.SearcherTaxonomyManager.SearcherAndTaxonomy;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.IOUtils;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util._TestUtil;
+
+public class TestSearcherTaxonomyManager extends LuceneTestCase {
+  public void test() throws Exception {
+    Directory dir = newDirectory();
+    Directory taxoDir = newDirectory();
+    final IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())));
+    final DirectoryTaxonomyWriter tw = new DirectoryTaxonomyWriter(taxoDir);
+    final FacetFields facetFields = new FacetFields(tw);
+    final AtomicBoolean stop = new AtomicBoolean();
+
+    Thread indexer = new Thread() {
+        @Override
+        public void run() {
+          Set<String> seen = new HashSet<String>();
+          List<String> paths = new ArrayList<String>();
+          while (!stop.get()) {
+            Document doc = new Document();
+            List<CategoryPath> docPaths = new ArrayList<CategoryPath>();
+            int numPaths = _TestUtil.nextInt(random(), 1, 5);
+            for(int i=0;i<numPaths;i++) {
+              String path;
+              if (!paths.isEmpty() && random().nextInt(5) != 4) {
+                // Use previous path
+                path = paths.get(random().nextInt(paths.size()));
+              } else {
+                // Create new path
+                path = null;
+                while (true) {
+                  path = _TestUtil.randomRealisticUnicodeString(random());
+                  if (path.length() != 0 && !seen.contains(path) && path.indexOf(FacetIndexingParams.DEFAULT_FACET_DELIM_CHAR) == -1) {
+                    seen.add(path);
+                    paths.add(path);
+                    break;
+                  }
+                }
+              }
+              docPaths.add(new CategoryPath("field", path));
+            }
+            try {
+              facetFields.addFields(doc, docPaths);
+              w.addDocument(doc);
+            } catch (IOException ioe) {
+              throw new RuntimeException(ioe);
+            }
+          }
+        }
+      };
+
+    final SearcherTaxonomyManager mgr = new SearcherTaxonomyManager(w, true, null, tw);
+
+    Thread reopener = new Thread() {
+        @Override
+        public void run() {
+          while(!stop.get()) {
+            try {
+              // Sleep for up to 20 msec:
+              Thread.sleep(random().nextInt(20));
+
+              if (VERBOSE) {
+                System.out.println("TEST: reopen");
+              }
+
+              mgr.maybeRefresh();
+
+              if (VERBOSE) {
+                System.out.println("TEST: reopen done");
+              }
+            } catch (Exception ioe) {
+              throw new RuntimeException(ioe);
+            }
+          }
+        }
+      };
+    reopener.start();
+
+    float runTimeSec = TEST_NIGHTLY ? 10.0f : 2.0f;
+
+    long stopTime = System.currentTimeMillis() + (int) (runTimeSec*1000);
+
+    indexer.start();
+
+    try {
+      while (System.currentTimeMillis() < stopTime) {
+        SearcherAndTaxonomy pair = mgr.acquire();
+        try {
+          //System.out.println("search maxOrd=" + pair.taxonomyReader.getSize());
+          int topN;
+          if (random().nextBoolean()) {
+            topN = _TestUtil.nextInt(random(), 1, 20);
+          } else {
+            topN = Integer.MAX_VALUE;
+          }
+          FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("field"), topN));
+          FacetsCollector fc = FacetsCollector.create(fsp, pair.searcher.getIndexReader(), pair.taxonomyReader);
+          pair.searcher.search(new MatchAllDocsQuery(), fc);
+          List<FacetResult> results = fc.getFacetResults();
+          FacetResult fr = results.get(0);
+          FacetResultNode root = results.get(0).getFacetResultNode();
+          assertTrue(root.ordinal != 0);
+
+          if (pair.searcher.getIndexReader().numDocs() > 0) { 
+            assertTrue(fr.getNumValidDescendants() > 0);
+            assertFalse(root.subResults.isEmpty());
+          }
+          //if (VERBOSE) {
+          //System.out.println("TEST: facets=" + FacetTestUtils.toSimpleString(results.get(0)));
+          //}
+        } finally {
+          mgr.release(pair);
+        }
+      }
+    } finally {
+      stop.set(true);
+      indexer.join();
+      reopener.join();
+    }
+
+    if (VERBOSE) {
+      System.out.println("TEST: now stop");
+    }
+
+    IOUtils.close(mgr, tw, w, taxoDir, dir);
+  }
+
+  public void testReplaceTaxonomy() throws Exception {
+    Directory dir = newDirectory();
+    Directory taxoDir = newDirectory();
+    IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())));
+    DirectoryTaxonomyWriter tw = new DirectoryTaxonomyWriter(taxoDir);
+
+    Directory taxoDir2 = newDirectory();
+    DirectoryTaxonomyWriter tw2 = new DirectoryTaxonomyWriter(taxoDir2);
+    tw2.close();
+
+    SearcherTaxonomyManager mgr = new SearcherTaxonomyManager(w, true, null, tw);
+    w.addDocument(new Document());
+    tw.replaceTaxonomy(taxoDir2);
+    taxoDir2.close();
+
+    try {
+      mgr.maybeRefresh();
+      fail("should have hit exception");
+    } catch (IllegalStateException ise) {
+      // expected
+    }
+
+    IOUtils.close(mgr, tw, w, taxoDir, dir);
+  }
+}

Modified: lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java?rev=1466548&r1=1466547&r2=1466548&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java (original)
+++ lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java Wed Apr 10 16:25:22 2013
@@ -448,7 +448,7 @@ public abstract class ShardSearchingTest
       currentNodeVersions = new long[numNodes];
     }
 
-    public void initSearcher(long[] nodeVersions) {
+    public void initSearcher(long[] nodeVersions) throws IOException {
       assert currentShardSearcher == null;
       System.arraycopy(nodeVersions, 0, currentNodeVersions, 0, currentNodeVersions.length);
       currentShardSearcher = new ShardIndexSearcher(currentNodeVersions.clone(),