| /* |
| * 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. |
| */ |
| |
| using System; |
| using System.Collections.Generic; |
| using Lucene.Net.Support; |
| using NUnit.Framework; |
| |
| using KeywordAnalyzer = Lucene.Net.Analysis.KeywordAnalyzer; |
| using WhitespaceAnalyzer = Lucene.Net.Analysis.WhitespaceAnalyzer; |
| using StandardAnalyzer = Lucene.Net.Analysis.Standard.StandardAnalyzer; |
| using Document = Lucene.Net.Documents.Document; |
| using Field = Lucene.Net.Documents.Field; |
| using Index = Lucene.Net.Documents.Field.Index; |
| using Store = Lucene.Net.Documents.Field.Store; |
| using MaxFieldLength = Lucene.Net.Index.IndexWriter.MaxFieldLength; |
| using AlreadyClosedException = Lucene.Net.Store.AlreadyClosedException; |
| using Directory = Lucene.Net.Store.Directory; |
| using FSDirectory = Lucene.Net.Store.FSDirectory; |
| using MockRAMDirectory = Lucene.Net.Store.MockRAMDirectory; |
| using BitVector = Lucene.Net.Util.BitVector; |
| using IndexSearcher = Lucene.Net.Search.IndexSearcher; |
| using ScoreDoc = Lucene.Net.Search.ScoreDoc; |
| using TermQuery = Lucene.Net.Search.TermQuery; |
| using LuceneTestCase = Lucene.Net.Util.LuceneTestCase; |
| |
| namespace Lucene.Net.Index |
| { |
| |
| [TestFixture] |
| public class TestIndexReaderReopen:LuceneTestCase |
| { |
| private class InjectableTestReopen : TestReopen |
| { |
| public Func<IndexReader> OpenReaderFunc { get; set; } |
| public Action<int> ModifyIndexAction { get; set; } |
| protected internal override IndexReader OpenReader() |
| { |
| return OpenReaderFunc.Invoke(); |
| } |
| |
| protected internal override void ModifyIndex(int i) |
| { |
| ModifyIndexAction.Invoke(i); |
| } |
| } |
| |
| private class AnonymousClassReaderThreadTask:ReaderThreadTask |
| { |
| public AnonymousClassReaderThreadTask(int index, Lucene.Net.Index.IndexReader r, Lucene.Net.Index.TestIndexReaderReopen.TestReopen test, System.Collections.Hashtable readersToClose, System.Collections.IList readers, System.Random rnd, TestIndexReaderReopen enclosingInstance) |
| { |
| InitBlock(index, r, test, readersToClose, readers, rnd, enclosingInstance); |
| } |
| private void InitBlock(int index, Lucene.Net.Index.IndexReader r, Lucene.Net.Index.TestIndexReaderReopen.TestReopen test, System.Collections.Hashtable readersToClose, System.Collections.IList readers, System.Random rnd, TestIndexReaderReopen enclosingInstance) |
| { |
| this.index = index; |
| this.r = r; |
| this.test = test; |
| this.readersToClose = readersToClose; |
| this.readers = readers; |
| this.rnd = rnd; |
| this.enclosingInstance = enclosingInstance; |
| } |
| private int index; |
| private Lucene.Net.Index.IndexReader r; |
| private Lucene.Net.Index.TestIndexReaderReopen.TestReopen test; |
| private System.Collections.Hashtable readersToClose; |
| private System.Collections.IList readers; |
| private System.Random rnd; |
| private TestIndexReaderReopen enclosingInstance; |
| public TestIndexReaderReopen Enclosing_Instance |
| { |
| get |
| { |
| return enclosingInstance; |
| } |
| |
| } |
| |
| public override void Run() |
| { |
| while (!stopped) |
| { |
| if (index % 2 == 0) |
| { |
| // refresh reader synchronized |
| ReaderCouple c = (Enclosing_Instance.RefreshReader(r, test, index, true)); |
| CollectionsHelper.AddIfNotContains(readersToClose, c.newReader); |
| CollectionsHelper.AddIfNotContains(readersToClose, c.refreshedReader); |
| readers.Add(c); |
| // prevent too many readers |
| break; |
| } |
| else |
| { |
| // not synchronized |
| IndexReader refreshed = r.Reopen(); |
| |
| |
| IndexSearcher searcher = new IndexSearcher(refreshed); |
| ScoreDoc[] hits = searcher.Search(new TermQuery(new Term("field1", "a" + rnd.Next(refreshed.MaxDoc))), null, 1000).ScoreDocs; |
| if (hits.Length > 0) |
| { |
| searcher.Doc(hits[0].Doc); |
| } |
| |
| // r might have changed because this is not a |
| // synchronized method. However we don't want |
| // to make it synchronized to test |
| // thread-safety of IndexReader.close(). |
| // That's why we add refreshed also to |
| // readersToClose, because double closing is fine |
| if (refreshed != r) |
| { |
| refreshed.Close(); |
| } |
| CollectionsHelper.AddIfNotContains(readersToClose, refreshed); |
| } |
| lock (this) |
| { |
| System.Threading.Monitor.Wait(this, TimeSpan.FromMilliseconds(1000)); |
| } |
| } |
| } |
| } |
| private class AnonymousClassReaderThreadTask1:ReaderThreadTask |
| { |
| public AnonymousClassReaderThreadTask1(System.Collections.IList readers, System.Random rnd, TestIndexReaderReopen enclosingInstance) |
| { |
| InitBlock(readers, rnd, enclosingInstance); |
| } |
| private void InitBlock(System.Collections.IList readers, System.Random rnd, TestIndexReaderReopen enclosingInstance) |
| { |
| this.readers = readers; |
| this.rnd = rnd; |
| this.enclosingInstance = enclosingInstance; |
| } |
| private System.Collections.IList readers; |
| private System.Random rnd; |
| private TestIndexReaderReopen enclosingInstance; |
| public TestIndexReaderReopen Enclosing_Instance |
| { |
| get |
| { |
| return enclosingInstance; |
| } |
| |
| } |
| public override void Run() |
| { |
| while (!stopped) |
| { |
| int numReaders = readers.Count; |
| if (numReaders > 0) |
| { |
| ReaderCouple c = (ReaderCouple) readers[rnd.Next(numReaders)]; |
| TestIndexReader.AssertIndexEquals(c.newReader, c.refreshedReader); |
| } |
| |
| lock (this) |
| { |
| System.Threading.Monitor.Wait(this, TimeSpan.FromMilliseconds(100)); |
| } |
| } |
| } |
| } |
| |
| private System.IO.DirectoryInfo indexDir; |
| |
| [Test] |
| public virtual void TestReopen_Renamed() |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| |
| CreateIndex(dir1, false); |
| PerformDefaultTests(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => ModifyIndex(i, dir1), |
| OpenReaderFunc = () => IndexReader.Open(dir1, false) |
| }); |
| dir1.Close(); |
| |
| Directory dir2 = new MockRAMDirectory(); |
| |
| CreateIndex(dir2, true); |
| PerformDefaultTests(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => ModifyIndex(i, dir2), |
| OpenReaderFunc = () => IndexReader.Open(dir2, false) |
| }); |
| dir2.Close(); |
| } |
| |
| [Test] |
| public virtual void TestParallelReaderReopen() |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, true); |
| Directory dir2 = new MockRAMDirectory(); |
| CreateIndex(dir2, true); |
| |
| PerformDefaultTests(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => |
| { |
| ModifyIndex(i, dir1); |
| ModifyIndex(i, dir2); |
| }, |
| OpenReaderFunc = () => |
| { |
| ParallelReader pr = new ParallelReader(); |
| pr.Add(IndexReader.Open(dir1, false)); |
| pr.Add(IndexReader.Open(dir2, false)); |
| return pr; |
| } |
| }); |
| dir1.Close(); |
| dir2.Close(); |
| |
| Directory dir3 = new MockRAMDirectory(); |
| CreateIndex(dir3, true); |
| Directory dir4 = new MockRAMDirectory(); |
| CreateIndex(dir4, true); |
| |
| PerformTestsWithExceptionInReopen(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => |
| { |
| ModifyIndex(i, dir3); |
| ModifyIndex(i, dir4); |
| }, |
| OpenReaderFunc = () => |
| { |
| ParallelReader pr = new ParallelReader(); |
| pr.Add(IndexReader.Open(dir3, false)); |
| pr.Add(IndexReader.Open(dir4, false)); |
| // Does not implement reopen, so |
| // hits exception: |
| pr.Add(new FilterIndexReader(IndexReader.Open(dir3, false))); |
| return pr; |
| } |
| }); |
| dir3.Close(); |
| dir4.Close(); |
| } |
| |
| // LUCENE-1228: IndexWriter.commit() does not update the index version |
| // populate an index in iterations. |
| // at the end of every iteration, commit the index and reopen/recreate the reader. |
| // in each iteration verify the work of previous iteration. |
| // try this once with reopen once recreate, on both RAMDir and FSDir. |
| [Test] |
| public virtual void TestCommitReopenFS() |
| { |
| Directory dir = FSDirectory.Open(indexDir); |
| DoTestReopenWithCommit(dir, true); |
| dir.Close(); |
| } |
| [Test] |
| public virtual void TestCommitRecreateFS() |
| { |
| Directory dir = FSDirectory.Open(indexDir); |
| DoTestReopenWithCommit(dir, false); |
| dir.Close(); |
| } |
| [Test] |
| public virtual void TestCommitReopenRAM() |
| { |
| Directory dir = new MockRAMDirectory(); |
| DoTestReopenWithCommit(dir, true); |
| dir.Close(); |
| } |
| [Test] |
| public virtual void TestCommitRecreateRAM() |
| { |
| Directory dir = new MockRAMDirectory(); |
| DoTestReopenWithCommit(dir, false); |
| } |
| |
| private void DoTestReopenWithCommit(Directory dir, bool withReopen) |
| { |
| IndexWriter iwriter = new IndexWriter(dir, new KeywordAnalyzer(), true, MaxFieldLength.LIMITED); |
| iwriter.SetMergeScheduler(new SerialMergeScheduler()); |
| IndexReader reader = IndexReader.Open(dir, false); |
| try |
| { |
| int M = 3; |
| for (int i = 0; i < 4; i++) |
| { |
| for (int j = 0; j < M; j++) |
| { |
| Document doc = new Document(); |
| doc.Add(new Field("id", i + "_" + j, Field.Store.YES, Field.Index.NOT_ANALYZED)); |
| doc.Add(new Field("id2", i + "_" + j, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS)); |
| doc.Add(new Field("id3", i + "_" + j, Field.Store.YES, Field.Index.NO)); |
| iwriter.AddDocument(doc); |
| if (i > 0) |
| { |
| int k = i - 1; |
| int n = j + k * M; |
| Document prevItereationDoc = reader.Document(n); |
| Assert.IsNotNull(prevItereationDoc); |
| System.String id = prevItereationDoc.Get("id"); |
| Assert.AreEqual(k + "_" + j, id); |
| } |
| } |
| iwriter.Commit(); |
| if (withReopen) |
| { |
| // reopen |
| IndexReader r2 = reader.Reopen(); |
| if (reader != r2) |
| { |
| reader.Close(); |
| reader = r2; |
| } |
| } |
| else |
| { |
| // recreate |
| reader.Close(); |
| reader = IndexReader.Open(dir, false); |
| } |
| } |
| } |
| finally |
| { |
| iwriter.Close(); |
| reader.Close(); |
| } |
| } |
| |
| [Test] |
| public virtual void TestMultiReaderReopen() |
| { |
| |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, true); |
| |
| Directory dir2 = new MockRAMDirectory(); |
| CreateIndex(dir2, true); |
| |
| PerformDefaultTests(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => |
| { |
| ModifyIndex(i, dir1); |
| ModifyIndex(i, dir2); |
| }, |
| OpenReaderFunc = () => new MultiReader(new[] |
| { |
| IndexReader.Open(dir1, false), |
| IndexReader.Open(dir2, false) |
| }) |
| }); |
| |
| dir1.Close(); |
| dir2.Close(); |
| |
| Directory dir3 = new MockRAMDirectory(); |
| CreateIndex(dir3, true); |
| |
| Directory dir4 = new MockRAMDirectory(); |
| CreateIndex(dir4, true); |
| |
| PerformTestsWithExceptionInReopen(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => |
| { |
| ModifyIndex(i, dir3); |
| ModifyIndex(i, dir4); |
| }, |
| OpenReaderFunc = () => new MultiReader(new[] |
| { |
| IndexReader.Open(dir3, false), |
| IndexReader.Open(dir4, false), |
| //does not implement reopen, |
| //so hits exception |
| new FilterIndexReader(IndexReader.Open(dir3, false)) |
| }) |
| }); |
| dir3.Close(); |
| dir4.Close(); |
| } |
| |
| [Test] |
| public virtual void TestMixedReaders() |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, true); |
| Directory dir2 = new MockRAMDirectory(); |
| CreateIndex(dir2, true); |
| Directory dir3 = new MockRAMDirectory(); |
| CreateIndex(dir3, false); |
| Directory dir4 = new MockRAMDirectory(); |
| CreateIndex(dir4, true); |
| Directory dir5 = new MockRAMDirectory(); |
| CreateIndex(dir5, false); |
| |
| //PerformDefaultTests(new AnonymousClassTestReopen6(dir1, dir4, dir5, dir2, dir3, this)); |
| PerformDefaultTests(new InjectableTestReopen |
| { |
| ModifyIndexAction = i => |
| { |
| // only change norms in this index to maintain the same number of docs for each of ParallelReader's subreaders |
| if (i == 1) ModifyIndex(i, dir1); |
| ModifyIndex(i, dir4); |
| ModifyIndex(i, dir5); |
| }, |
| OpenReaderFunc = () => |
| { |
| ParallelReader pr = new ParallelReader(); |
| pr.Add(IndexReader.Open(dir1, false)); |
| pr.Add(IndexReader.Open(dir2, false)); |
| MultiReader mr = new MultiReader(new []{IndexReader.Open(dir3, false), IndexReader.Open(dir4, false)}); |
| return new MultiReader(new[] |
| { |
| pr, mr, |
| IndexReader.Open(dir5, false) |
| }); |
| } |
| }); |
| dir1.Close(); |
| dir2.Close(); |
| dir3.Close(); |
| dir4.Close(); |
| dir5.Close(); |
| } |
| |
| private void PerformDefaultTests(TestReopen test) |
| { |
| |
| IndexReader index1 = test.OpenReader(); |
| IndexReader index2 = test.OpenReader(); |
| |
| TestIndexReader.AssertIndexEquals(index1, index2); |
| |
| // verify that reopen() does not return a new reader instance |
| // in case the index has no changes |
| ReaderCouple couple = RefreshReader(index2, false); |
| Assert.IsTrue(couple.refreshedReader == index2); |
| |
| couple = RefreshReader(index2, test, 0, true); |
| index1.Close(); |
| index1 = couple.newReader; |
| |
| IndexReader index2_refreshed = couple.refreshedReader; |
| index2.Close(); |
| |
| // test if refreshed reader and newly opened reader return equal results |
| TestIndexReader.AssertIndexEquals(index1, index2_refreshed); |
| |
| index2_refreshed.Close(); |
| AssertReaderClosed(index2, true, true); |
| AssertReaderClosed(index2_refreshed, true, true); |
| |
| index2 = test.OpenReader(); |
| |
| for (int i = 1; i < 4; i++) |
| { |
| |
| index1.Close(); |
| couple = RefreshReader(index2, test, i, true); |
| // refresh IndexReader |
| index2.Close(); |
| |
| index2 = couple.refreshedReader; |
| index1 = couple.newReader; |
| TestIndexReader.AssertIndexEquals(index1, index2); |
| } |
| |
| index1.Close(); |
| index2.Close(); |
| AssertReaderClosed(index1, true, true); |
| AssertReaderClosed(index2, true, true); |
| } |
| |
| [Test] |
| public virtual void TestReferenceCounting() |
| { |
| |
| for (int mode = 0; mode < 4; mode++) |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, true); |
| |
| IndexReader reader0 = IndexReader.Open(dir1, false); |
| AssertRefCountEquals(1, reader0); |
| |
| Assert.IsTrue(reader0 is DirectoryReader); |
| IndexReader[] subReaders0 = reader0.GetSequentialSubReaders(); |
| for (int i = 0; i < subReaders0.Length; i++) |
| { |
| AssertRefCountEquals(1, subReaders0[i]); |
| } |
| |
| // delete first document, so that only one of the subReaders have to be re-opened |
| IndexReader modifier = IndexReader.Open(dir1, false); |
| modifier.DeleteDocument(0); |
| modifier.Close(); |
| |
| IndexReader reader1 = RefreshReader(reader0, true).refreshedReader; |
| Assert.IsTrue(reader1 is DirectoryReader); |
| IndexReader[] subReaders1 = reader1.GetSequentialSubReaders(); |
| Assert.AreEqual(subReaders0.Length, subReaders1.Length); |
| |
| for (int i = 0; i < subReaders0.Length; i++) |
| { |
| if (subReaders0[i] != subReaders1[i]) |
| { |
| AssertRefCountEquals(1, subReaders0[i]); |
| AssertRefCountEquals(1, subReaders1[i]); |
| } |
| else |
| { |
| AssertRefCountEquals(2, subReaders0[i]); |
| } |
| } |
| |
| // delete first document, so that only one of the subReaders have to be re-opened |
| modifier = IndexReader.Open(dir1, false); |
| modifier.DeleteDocument(1); |
| modifier.Close(); |
| |
| IndexReader reader2 = RefreshReader(reader1, true).refreshedReader; |
| Assert.IsTrue(reader2 is DirectoryReader); |
| IndexReader[] subReaders2 = reader2.GetSequentialSubReaders(); |
| Assert.AreEqual(subReaders1.Length, subReaders2.Length); |
| |
| for (int i = 0; i < subReaders2.Length; i++) |
| { |
| if (subReaders2[i] == subReaders1[i]) |
| { |
| if (subReaders1[i] == subReaders0[i]) |
| { |
| AssertRefCountEquals(3, subReaders2[i]); |
| } |
| else |
| { |
| AssertRefCountEquals(2, subReaders2[i]); |
| } |
| } |
| else |
| { |
| AssertRefCountEquals(1, subReaders2[i]); |
| if (subReaders0[i] == subReaders1[i]) |
| { |
| AssertRefCountEquals(2, subReaders2[i]); |
| AssertRefCountEquals(2, subReaders0[i]); |
| } |
| else |
| { |
| AssertRefCountEquals(1, subReaders0[i]); |
| AssertRefCountEquals(1, subReaders1[i]); |
| } |
| } |
| } |
| |
| IndexReader reader3 = RefreshReader(reader0, true).refreshedReader; |
| Assert.IsTrue(reader3 is DirectoryReader); |
| IndexReader[] subReaders3 = reader3.GetSequentialSubReaders(); |
| Assert.AreEqual(subReaders3.Length, subReaders0.Length); |
| |
| // try some permutations |
| switch (mode) |
| { |
| |
| case 0: |
| reader0.Close(); |
| reader1.Close(); |
| reader2.Close(); |
| reader3.Close(); |
| break; |
| |
| case 1: |
| reader3.Close(); |
| reader2.Close(); |
| reader1.Close(); |
| reader0.Close(); |
| break; |
| |
| case 2: |
| reader2.Close(); |
| reader3.Close(); |
| reader0.Close(); |
| reader1.Close(); |
| break; |
| |
| case 3: |
| reader1.Close(); |
| reader3.Close(); |
| reader2.Close(); |
| reader0.Close(); |
| break; |
| } |
| |
| AssertReaderClosed(reader0, true, true); |
| AssertReaderClosed(reader1, true, true); |
| AssertReaderClosed(reader2, true, true); |
| AssertReaderClosed(reader3, true, true); |
| |
| dir1.Close(); |
| } |
| } |
| |
| |
| [Test] |
| public virtual void TestReferenceCountingMultiReader() |
| { |
| for (int mode = 0; mode <= 1; mode++) |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, false); |
| Directory dir2 = new MockRAMDirectory(); |
| CreateIndex(dir2, true); |
| |
| IndexReader reader1 = IndexReader.Open(dir1, false); |
| AssertRefCountEquals(1, reader1); |
| |
| IndexReader initReader2 = IndexReader.Open(dir2, false); |
| IndexReader multiReader1 = new MultiReader(new IndexReader[]{reader1, initReader2}, (mode == 0)); |
| ModifyIndex(0, dir2); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| IndexReader multiReader2 = multiReader1.Reopen(); |
| // index1 hasn't changed, so multiReader2 should share reader1 now with multiReader1 |
| AssertRefCountEquals(2 + mode, reader1); |
| |
| ModifyIndex(0, dir1); |
| IndexReader reader2 = reader1.Reopen(); |
| AssertRefCountEquals(2 + mode, reader1); |
| |
| if (mode == 1) |
| { |
| initReader2.Close(); |
| } |
| |
| ModifyIndex(1, dir1); |
| IndexReader reader3 = reader2.Reopen(); |
| AssertRefCountEquals(2 + mode, reader1); |
| AssertRefCountEquals(1, reader2); |
| |
| multiReader1.Close(); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| multiReader1.Close(); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| if (mode == 1) |
| { |
| initReader2.Close(); |
| } |
| |
| reader1.Close(); |
| AssertRefCountEquals(1, reader1); |
| |
| multiReader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| multiReader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| reader3.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, false); |
| |
| reader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, false); |
| |
| reader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| reader3.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, true); |
| dir1.Close(); |
| dir2.Close(); |
| } |
| } |
| |
| [Test] |
| public virtual void TestReferenceCountingParallelReader() |
| { |
| for (int mode = 0; mode <= 1; mode++) |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, false); |
| Directory dir2 = new MockRAMDirectory(); |
| CreateIndex(dir2, true); |
| |
| IndexReader reader1 = IndexReader.Open(dir1, false); |
| AssertRefCountEquals(1, reader1); |
| |
| ParallelReader parallelReader1 = new ParallelReader(mode == 0); |
| parallelReader1.Add(reader1); |
| IndexReader initReader2 = IndexReader.Open(dir2, false); |
| parallelReader1.Add(initReader2); |
| ModifyIndex(1, dir2); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| IndexReader parallelReader2 = parallelReader1.Reopen(); |
| // index1 hasn't changed, so parallelReader2 should share reader1 now with multiReader1 |
| AssertRefCountEquals(2 + mode, reader1); |
| |
| ModifyIndex(0, dir1); |
| ModifyIndex(0, dir2); |
| IndexReader reader2 = reader1.Reopen(); |
| AssertRefCountEquals(2 + mode, reader1); |
| |
| if (mode == 1) |
| { |
| initReader2.Close(); |
| } |
| |
| ModifyIndex(4, dir1); |
| IndexReader reader3 = reader2.Reopen(); |
| AssertRefCountEquals(2 + mode, reader1); |
| AssertRefCountEquals(1, reader2); |
| |
| parallelReader1.Close(); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| parallelReader1.Close(); |
| AssertRefCountEquals(1 + mode, reader1); |
| |
| if (mode == 1) |
| { |
| initReader2.Close(); |
| } |
| |
| reader1.Close(); |
| AssertRefCountEquals(1, reader1); |
| |
| parallelReader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| parallelReader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| reader3.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, false); |
| |
| reader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, false); |
| |
| reader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| reader3.Close(); |
| AssertRefCountEquals(0, reader1); |
| AssertReaderClosed(reader1, true, true); |
| |
| dir1.Close(); |
| dir2.Close(); |
| } |
| } |
| |
| [Test] |
| public virtual void TestNormsRefCounting() |
| { |
| Directory dir1 = new MockRAMDirectory(); |
| CreateIndex(dir1, false); |
| |
| IndexReader reader1 = IndexReader.Open(dir1, false); |
| SegmentReader segmentReader1 = SegmentReader.GetOnlySegmentReader(reader1); |
| IndexReader modifier = IndexReader.Open(dir1, false); |
| modifier.DeleteDocument(0); |
| modifier.Close(); |
| |
| IndexReader reader2 = reader1.Reopen(); |
| modifier = IndexReader.Open(dir1, false); |
| modifier.SetNorm(1, "field1", 50); |
| modifier.SetNorm(1, "field2", 50); |
| modifier.Close(); |
| |
| IndexReader reader3 = reader2.Reopen(); |
| SegmentReader segmentReader3 = SegmentReader.GetOnlySegmentReader(reader3); |
| modifier = IndexReader.Open(dir1, false); |
| modifier.DeleteDocument(2); |
| modifier.Close(); |
| |
| IndexReader reader4 = reader3.Reopen(); |
| modifier = IndexReader.Open(dir1, false); |
| modifier.DeleteDocument(3); |
| modifier.Close(); |
| |
| IndexReader reader5 = reader3.Reopen(); |
| |
| // Now reader2-reader5 references reader1. reader1 and reader2 |
| // share the same norms. reader3, reader4, reader5 also share norms. |
| AssertRefCountEquals(1, reader1); |
| Assert.IsFalse(segmentReader1.NormsClosed()); |
| |
| reader1.Close(); |
| |
| AssertRefCountEquals(0, reader1); |
| Assert.IsFalse(segmentReader1.NormsClosed()); |
| |
| reader2.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| // now the norms for field1 and field2 should be closed |
| Assert.IsTrue(segmentReader1.NormsClosed("field1")); |
| Assert.IsTrue(segmentReader1.NormsClosed("field2")); |
| |
| // but the norms for field3 and field4 should still be open |
| Assert.IsFalse(segmentReader1.NormsClosed("field3")); |
| Assert.IsFalse(segmentReader1.NormsClosed("field4")); |
| |
| reader3.Close(); |
| AssertRefCountEquals(0, reader1); |
| Assert.IsFalse(segmentReader3.NormsClosed()); |
| reader5.Close(); |
| AssertRefCountEquals(0, reader1); |
| Assert.IsFalse(segmentReader3.NormsClosed()); |
| reader4.Close(); |
| AssertRefCountEquals(0, reader1); |
| |
| // and now all norms that reader1 used should be closed |
| Assert.IsTrue(segmentReader1.NormsClosed()); |
| |
| // now that reader3, reader4 and reader5 are closed, |
| // the norms that those three readers shared should be |
| // closed as well |
| Assert.IsTrue(segmentReader3.NormsClosed()); |
| |
| dir1.Close(); |
| } |
| |
| private void PerformTestsWithExceptionInReopen(TestReopen test) |
| { |
| IndexReader index1 = test.OpenReader(); |
| IndexReader index2 = test.OpenReader(); |
| |
| TestIndexReader.AssertIndexEquals(index1, index2); |
| |
| Assert.Throws(Is.InstanceOf<Exception>(), () => RefreshReader(index1, test, 0, true), "Expected exception not thrown."); |
| |
| // index2 should still be usable and unaffected by the failed reopen() call |
| TestIndexReader.AssertIndexEquals(index1, index2); |
| |
| index1.Close(); |
| index2.Close(); |
| } |
| |
| [Test] |
| public virtual void TestThreadSafety() |
| { |
| Directory dir = new MockRAMDirectory(); |
| int n = 150; |
| |
| IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(Util.Version.LUCENE_CURRENT), IndexWriter.MaxFieldLength.LIMITED); |
| for (int i = 0; i < n; i++) |
| { |
| writer.AddDocument(CreateDocument(i, 3)); |
| } |
| writer.Optimize(); |
| writer.Close(); |
| |
| TestReopen test = new InjectableTestReopen |
| { |
| OpenReaderFunc = () => IndexReader.Open(dir, false), |
| ModifyIndexAction = i => |
| { |
| if (i%3 == 0) |
| { |
| IndexReader modifier = IndexReader.Open(dir, false); |
| modifier.SetNorm(i, "field1", 50); |
| modifier.Close(); |
| } |
| else if (i%3 == 1) |
| { |
| IndexReader modifier = IndexReader.Open(dir, false); |
| modifier.DeleteDocument(i%modifier.MaxDoc); |
| modifier.Close(); |
| } |
| else |
| { |
| IndexWriter modifier = new IndexWriter(dir, |
| new StandardAnalyzer |
| (Util.Version |
| . |
| LUCENE_CURRENT), |
| IndexWriter. |
| MaxFieldLength |
| .LIMITED); |
| modifier.AddDocument(CreateDocument(n + i, 6)); |
| modifier.Close(); |
| } |
| }}; |
| |
| System.Collections.IList readers = (System.Collections.IList) System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(new System.Collections.ArrayList())); |
| IndexReader firstReader = IndexReader.Open(dir, false); |
| IndexReader reader = firstReader; |
| System.Random rnd = NewRandom(); |
| |
| ReaderThread[] threads = new ReaderThread[n]; |
| System.Collections.Hashtable readersToClose = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); |
| |
| for (int i = 0; i < n; i++) |
| { |
| if (i % 10 == 0) |
| { |
| IndexReader refreshed = reader.Reopen(); |
| if (refreshed != reader) |
| { |
| CollectionsHelper.AddIfNotContains(readersToClose, reader); |
| } |
| reader = refreshed; |
| } |
| IndexReader r = reader; |
| |
| int index = i; |
| |
| ReaderThreadTask task; |
| |
| if (i < 20 || (i >= 50 && i < 70) || i > 90) |
| { |
| task = new AnonymousClassReaderThreadTask(index, r, test, readersToClose, readers, rnd, this); |
| } |
| else |
| { |
| task = new AnonymousClassReaderThreadTask1(readers, rnd, this); |
| } |
| |
| threads[i] = new ReaderThread(task); |
| threads[i].Start(); |
| } |
| |
| lock (this) |
| { |
| System.Threading.Monitor.Wait(this, 15000); |
| } |
| |
| for (int i = 0; i < n; i++) |
| { |
| if (threads[i] != null) |
| { |
| threads[i].StopThread(); |
| } |
| } |
| |
| for (int i = 0; i < n; i++) |
| { |
| if (threads[i] != null) |
| { |
| threads[i].Join(); |
| if (threads[i].error != null) |
| { |
| Assert.Fail("Error occurred in thread " + threads[i].Name + ":\n" + threads[i].error.Message); |
| } |
| } |
| } |
| |
| System.Collections.IEnumerator it = readersToClose.GetEnumerator(); |
| while (it.MoveNext()) |
| { |
| ((IndexReader) ((System.Collections.DictionaryEntry)it.Current).Key).Close(); |
| } |
| |
| firstReader.Close(); |
| reader.Close(); |
| |
| it = readersToClose.GetEnumerator(); |
| while (it.MoveNext()) |
| { |
| AssertReaderClosed((IndexReader)((System.Collections.DictionaryEntry)it.Current).Key, true, true); |
| } |
| |
| AssertReaderClosed(reader, true, true); |
| AssertReaderClosed(firstReader, true, true); |
| |
| dir.Close(); |
| } |
| |
| internal class ReaderCouple |
| { |
| internal ReaderCouple(IndexReader r1, IndexReader r2) |
| { |
| newReader = r1; |
| refreshedReader = r2; |
| } |
| |
| internal IndexReader newReader; |
| internal IndexReader refreshedReader; |
| } |
| |
| abstract internal class ReaderThreadTask |
| { |
| protected internal bool stopped; |
| public virtual void Stop() |
| { |
| this.stopped = true; |
| } |
| |
| public abstract void Run(); |
| } |
| |
| private class ReaderThread:ThreadClass |
| { |
| private ReaderThreadTask task; |
| internal /*private*/ System.Exception error; |
| |
| |
| internal ReaderThread(ReaderThreadTask task) |
| { |
| this.task = task; |
| } |
| |
| public virtual void StopThread() |
| { |
| this.task.Stop(); |
| } |
| |
| override public void Run() |
| { |
| try |
| { |
| this.task.Run(); |
| } |
| catch (System.Exception r) |
| { |
| System.Console.Out.WriteLine(r.StackTrace); |
| this.error = r; |
| } |
| } |
| } |
| |
| private System.Object createReaderMutex = new System.Object(); |
| |
| private ReaderCouple RefreshReader(IndexReader reader, bool hasChanges) |
| { |
| return RefreshReader(reader, null, - 1, hasChanges); |
| } |
| |
| internal virtual ReaderCouple RefreshReader(IndexReader reader, TestReopen test, int modify, bool hasChanges) |
| { |
| lock (createReaderMutex) |
| { |
| IndexReader r = null; |
| if (test != null) |
| { |
| test.ModifyIndex(modify); |
| r = test.OpenReader(); |
| } |
| |
| IndexReader refreshed = null; |
| try |
| { |
| refreshed = reader.Reopen(); |
| } |
| finally |
| { |
| if (refreshed == null && r != null) |
| { |
| // Hit exception -- close opened reader |
| r.Close(); |
| } |
| } |
| |
| if (hasChanges) |
| { |
| if (refreshed == reader) |
| { |
| Assert.Fail("No new IndexReader instance created during refresh."); |
| } |
| } |
| else |
| { |
| if (refreshed != reader) |
| { |
| Assert.Fail("New IndexReader instance created during refresh even though index had no changes."); |
| } |
| } |
| |
| return new ReaderCouple(r, refreshed); |
| } |
| } |
| |
| public static void CreateIndex(Directory dir, bool multiSegment) |
| { |
| IndexWriter.Unlock(dir); |
| IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); |
| |
| w.SetMergePolicy(new LogDocMergePolicy(w)); |
| |
| for (int i = 0; i < 100; i++) |
| { |
| w.AddDocument(CreateDocument(i, 4)); |
| if (multiSegment && (i % 10) == 0) |
| { |
| w.Commit(); |
| } |
| } |
| |
| if (!multiSegment) |
| { |
| w.Optimize(); |
| } |
| |
| w.Close(); |
| |
| IndexReader r = IndexReader.Open(dir, false); |
| if (multiSegment) |
| { |
| Assert.IsTrue(r.GetSequentialSubReaders().Length > 1); |
| } |
| else |
| { |
| Assert.IsTrue(r.GetSequentialSubReaders().Length == 1); |
| } |
| r.Close(); |
| } |
| |
| public static Document CreateDocument(int n, int numFields) |
| { |
| System.Text.StringBuilder sb = new System.Text.StringBuilder(); |
| Document doc = new Document(); |
| sb.Append("a"); |
| sb.Append(n); |
| doc.Add(new Field("field1", sb.ToString(), Field.Store.YES, Field.Index.ANALYZED)); |
| doc.Add(new Field("fielda", sb.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS)); |
| doc.Add(new Field("fieldb", sb.ToString(), Field.Store.YES, Field.Index.NO)); |
| sb.Append(" b"); |
| sb.Append(n); |
| for (int i = 1; i < numFields; i++) |
| { |
| doc.Add(new Field("field" + (i + 1), sb.ToString(), Field.Store.YES, Field.Index.ANALYZED)); |
| } |
| return doc; |
| } |
| |
| internal static void ModifyIndex(int i, Directory dir) |
| { |
| switch (i) |
| { |
| |
| case 0: { |
| IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); |
| w.DeleteDocuments(new Term("field2", "a11")); |
| w.DeleteDocuments(new Term("field2", "b30")); |
| w.Close(); |
| break; |
| } |
| |
| case 1: { |
| IndexReader reader = IndexReader.Open(dir, false); |
| reader.SetNorm(4, "field1", 123); |
| reader.SetNorm(44, "field2", 222); |
| reader.SetNorm(44, "field4", 22); |
| reader.Close(); |
| break; |
| } |
| |
| case 2: { |
| IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); |
| w.Optimize(); |
| w.Close(); |
| break; |
| } |
| |
| case 3: { |
| IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); |
| w.AddDocument(CreateDocument(101, 4)); |
| w.Optimize(); |
| w.AddDocument(CreateDocument(102, 4)); |
| w.AddDocument(CreateDocument(103, 4)); |
| w.Close(); |
| break; |
| } |
| |
| case 4: { |
| IndexReader reader = IndexReader.Open(dir, false); |
| reader.SetNorm(5, "field1", 123); |
| reader.SetNorm(55, "field2", 222); |
| reader.Close(); |
| break; |
| } |
| |
| case 5: { |
| IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); |
| w.AddDocument(CreateDocument(101, 4)); |
| w.Close(); |
| break; |
| } |
| } |
| } |
| |
| private void AssertReaderClosed(IndexReader reader, bool checkSubReaders, bool checkNormsClosed) |
| { |
| Assert.AreEqual(0, reader.RefCount); |
| |
| if (checkNormsClosed && reader is SegmentReader) |
| { |
| Assert.IsTrue(((SegmentReader) reader).NormsClosed()); |
| } |
| |
| if (checkSubReaders) |
| { |
| if (reader is DirectoryReader) |
| { |
| IndexReader[] subReaders = reader.GetSequentialSubReaders(); |
| for (int i = 0; i < subReaders.Length; i++) |
| { |
| AssertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed); |
| } |
| } |
| |
| if (reader is MultiReader) |
| { |
| IndexReader[] subReaders = reader.GetSequentialSubReaders(); |
| for (int i = 0; i < subReaders.Length; i++) |
| { |
| AssertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed); |
| } |
| } |
| |
| if (reader is ParallelReader) |
| { |
| IndexReader[] subReaders = ((ParallelReader) reader).GetSubReaders(); |
| for (int i = 0; i < subReaders.Length; i++) |
| { |
| AssertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed); |
| } |
| } |
| } |
| } |
| |
| /* |
| private void assertReaderOpen(IndexReader reader) { |
| reader.ensureOpen(); |
| |
| if (reader instanceof DirectoryReader) { |
| IndexReader[] subReaders = reader.getSequentialSubReaders(); |
| for (int i = 0; i < subReaders.length; i++) { |
| assertReaderOpen(subReaders[i]); |
| } |
| } |
| } |
| */ |
| |
| private void AssertRefCountEquals(int refCount, IndexReader reader) |
| { |
| Assert.AreEqual(refCount, reader.RefCount, "Reader has wrong refCount value."); |
| } |
| |
| |
| abstract internal class TestReopen |
| { |
| protected internal abstract IndexReader OpenReader(); |
| protected internal abstract void ModifyIndex(int i); |
| } |
| |
| |
| [SetUp] |
| public override void SetUp() |
| { |
| base.SetUp(); |
| System.String tempDir = System.IO.Path.GetTempPath(); |
| if (tempDir == null) |
| throw new System.IO.IOException("java.io.tmpdir undefined, cannot run test"); |
| indexDir = new System.IO.DirectoryInfo(System.IO.Path.Combine(tempDir, "IndexReaderReopen")); |
| } |
| [Test] |
| public virtual void TestCloseOrig() |
| { |
| Directory dir = new MockRAMDirectory(); |
| CreateIndex(dir, false); |
| IndexReader r1 = IndexReader.Open(dir, false); |
| IndexReader r2 = IndexReader.Open(dir, false); |
| r2.DeleteDocument(0); |
| r2.Close(); |
| |
| IndexReader r3 = r1.Reopen(); |
| Assert.IsTrue(r1 != r3); |
| r1.Close(); |
| Assert.Throws<AlreadyClosedException>(() => r1.Document(2), "did not hit exception"); |
| r3.Close(); |
| dir.Close(); |
| } |
| |
| [Test] |
| public virtual void TestDeletes() |
| { |
| Directory dir = new MockRAMDirectory(); |
| CreateIndex(dir, false); // Create an index with a bunch of docs (1 segment) |
| |
| ModifyIndex(0, dir); // Get delete bitVector on 1st segment |
| ModifyIndex(5, dir); // Add a doc (2 segments) |
| |
| IndexReader r1 = IndexReader.Open(dir, false); // MSR |
| |
| ModifyIndex(5, dir); // Add another doc (3 segments) |
| |
| IndexReader r2 = r1.Reopen(); // MSR |
| Assert.IsTrue(r1 != r2); |
| |
| SegmentReader sr1 = (SegmentReader) r1.GetSequentialSubReaders()[0]; // Get SRs for the first segment from original |
| SegmentReader sr2 = (SegmentReader) r2.GetSequentialSubReaders()[0]; // and reopened IRs |
| |
| // At this point they share the same BitVector |
| Assert.IsTrue(sr1.deletedDocs_ForNUnit == sr2.deletedDocs_ForNUnit); |
| |
| r2.DeleteDocument(0); |
| |
| // r1 should not see the delete |
| Assert.IsFalse(r1.IsDeleted(0)); |
| |
| // Now r2 should have made a private copy of deleted docs: |
| Assert.IsTrue(sr1.deletedDocs_ForNUnit != sr2.deletedDocs_ForNUnit); |
| |
| r1.Close(); |
| r2.Close(); |
| dir.Close(); |
| } |
| |
| [Test] |
| public virtual void TestDeletes2() |
| { |
| Directory dir = new MockRAMDirectory(); |
| CreateIndex(dir, false); |
| // Get delete bitVector |
| ModifyIndex(0, dir); |
| IndexReader r1 = IndexReader.Open(dir, false); |
| |
| // Add doc: |
| ModifyIndex(5, dir); |
| |
| IndexReader r2 = r1.Reopen(); |
| Assert.IsTrue(r1 != r2); |
| |
| IndexReader[] rs2 = r2.GetSequentialSubReaders(); |
| |
| SegmentReader sr1 = SegmentReader.GetOnlySegmentReader(r1); |
| SegmentReader sr2 = (SegmentReader) rs2[0]; |
| |
| // At this point they share the same BitVector |
| Assert.IsTrue(sr1.deletedDocs_ForNUnit == sr2.deletedDocs_ForNUnit); |
| BitVector delDocs = sr1.deletedDocs_ForNUnit; |
| r1.Close(); |
| |
| r2.DeleteDocument(0); |
| Assert.IsTrue(delDocs == sr2.deletedDocs_ForNUnit); |
| r2.Close(); |
| dir.Close(); |
| } |
| |
| private class KeepAllCommits : IndexDeletionPolicy |
| { |
| public virtual void OnInit<T>(IList<T> commits) where T : IndexCommit |
| { |
| } |
| public virtual void OnCommit<T>(IList<T> commits) where T : IndexCommit |
| { |
| } |
| } |
| |
| [Test] |
| public virtual void TestReopenOnCommit() |
| { |
| Directory dir = new MockRAMDirectory(); |
| IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), new KeepAllCommits(), IndexWriter.MaxFieldLength.UNLIMITED); |
| for (int i = 0; i < 4; i++) |
| { |
| Document doc = new Document(); |
| doc.Add(new Field("id", "" + i, Field.Store.NO, Field.Index.NOT_ANALYZED)); |
| writer.AddDocument(doc); |
| System.Collections.Generic.IDictionary<string, string> data = new System.Collections.Generic.Dictionary<string, string>(); |
| data["index"] = i + ""; |
| writer.Commit(data); |
| } |
| for (int i = 0; i < 4; i++) |
| { |
| writer.DeleteDocuments(new Term("id", "" + i)); |
| System.Collections.Generic.IDictionary<string, string> data = new System.Collections.Generic.Dictionary<string,string>(); |
| data["index"] = (4 + i) + ""; |
| writer.Commit(data); |
| } |
| writer.Close(); |
| |
| IndexReader r = IndexReader.Open(dir, false); |
| Assert.AreEqual(0, r.NumDocs()); |
| Assert.AreEqual(4, r.MaxDoc); |
| |
| System.Collections.IEnumerator it = IndexReader.ListCommits(dir).GetEnumerator(); |
| while (it.MoveNext()) |
| { |
| IndexCommit commit = (IndexCommit) it.Current; |
| IndexReader r2 = r.Reopen(commit); |
| Assert.IsTrue(r2 != r); |
| |
| // Reader should be readOnly |
| Assert.Throws<NotSupportedException>(() => r2.DeleteDocument(0), "no exception hit"); |
| |
| System.Collections.Generic.IDictionary<string, string> s = commit.UserData; |
| int v; |
| if (s.Count == 0) |
| { |
| // First commit created by IW |
| v = - 1; |
| } |
| else |
| { |
| v = System.Int32.Parse((System.String) s["index"]); |
| } |
| if (v < 4) |
| { |
| Assert.AreEqual(1 + v, r2.NumDocs()); |
| } |
| else |
| { |
| Assert.AreEqual(7 - v, r2.NumDocs()); |
| } |
| r.Close(); |
| r = r2; |
| } |
| r.Close(); |
| dir.Close(); |
| } |
| } |
| } |