Index: lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java =================================================================== --- lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java (revision 1466216) +++ lucene/core/src/java/org/apache/lucene/search/ReferenceManager.java (working copy) @@ -81,7 +81,7 @@ * 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 @@ * 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) { Index: lucene/core/src/java/org/apache/lucene/search/SearcherManager.java =================================================================== --- lucene/core/src/java/org/apache/lucene/search/SearcherManager.java (revision 1466216) +++ lucene/core/src/java/org/apache/lucene/search/SearcherManager.java (working copy) @@ -144,8 +144,11 @@ } } - // 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 { Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java (revision 0) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java (working copy) @@ -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.LuceneTestCase; +import org.apache.lucene.util._TestUtil; + +public class TestSearcherTaxonomyManager extends LuceneTestCase { + public void test() throws Exception { + Directory dir = newFSDirectory(_TestUtil.getTempDir("TestSearcherTaxonomyReader.index")); + Directory taxoDir = newFSDirectory(_TestUtil.getTempDir("TestSearcherTaxonomyReader.taxoIndex")); + 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 seen = new HashSet(); + List paths = new ArrayList(); + while (!stop.get()) { + Document doc = new Document(); + List docPaths = new ArrayList(); + int numPaths = _TestUtil.nextInt(random(), 1, 5); + for(int i=0;i results = fc.getFacetResults(); + assertEquals(1, results.size()); + //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"); + } + + mgr.close(); + w.close(); + tw.close(); + dir.close(); + taxoDir.close(); + } + + public void testReplaceTaxonomy() throws Exception { + Directory dir = newFSDirectory(_TestUtil.getTempDir("TestSearcherTaxonomyReader.index")); + Directory taxoDir = newFSDirectory(_TestUtil.getTempDir("TestSearcherTaxonomyReader.taxoIndex")); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()))); + DirectoryTaxonomyWriter tw = new DirectoryTaxonomyWriter(taxoDir); + + Directory taxoDir2 = newFSDirectory(_TestUtil.getTempDir("TestSearcherTaxonomyReader.taxoIndex2")); + 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 + } + + mgr.close(); + tw.close(); + w.close(); + taxoDir.close(); + dir.close(); + } +} Property changes on: lucene/facet/src/test/org/apache/lucene/facet/search/TestSearcherTaxonomyManager.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java (revision 1466216) +++ lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyReader.java (working copy) @@ -246,4 +246,16 @@ 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; + } } Index: lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java (revision 1466216) +++ lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java (working copy) @@ -9,7 +9,7 @@ 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; @@ -217,6 +217,14 @@ return DirectoryReader.open(writer, false); } + /** Expert: returns current index epoch, if this is a + * near-real-time reader. + * + * @lucene.internal */ + public long getTaxonomyEpoch() { + return taxoEpoch; + } + /** * Expert: returns the underlying {@link DirectoryReader} instance that is * used by this {@link TaxonomyReader}. Index: lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (revision 1466216) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (working copy) @@ -83,6 +83,7 @@ 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 { Index: lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java (working copy) @@ -0,0 +1,112 @@ +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; + +/** + * Manages near-real-time reopen of both an IndexSearcher + * and a TaxonomyReader. + * + *

NOTE: If you call {@link + * DirectoryTaxonomyWriter#replaceTaxonomy} then you must + * open a new {@code SearcherTaxonomyManager} afterwards. + */ +public class SearcherTaxonomyManager extends ReferenceManager { + + /** 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; + + /** 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; + DirectoryTaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoWriter); + taxoEpoch = taxoReader.getTaxonomyEpoch(); + current = new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, DirectoryReader.open(writer, applyAllDeletes)), + taxoReader); + } + + @Override + protected void decRef(SearcherAndTaxonomy ref) throws IOException { + ref.searcher.getIndexReader().decRef(); + 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 (tr.getTaxonomyEpoch() != taxoEpoch) { + newReader.decRef(); + tr.decRef(); + throw new IllegalStateException("DirectoryTaxonomyWriter.replaceTaxonomy was called, which is not allowed when using SearcherTaxonomyManager"); + } + + return new SearcherAndTaxonomy(SearcherManager.getSearcher(searcherFactory, newReader), tr); + } + } +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/SearcherTaxonomyManager.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java =================================================================== --- lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java (revision 1466216) +++ lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java (working copy) @@ -448,7 +448,7 @@ 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(),