1 package org.apache.lucene.index;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
28 import java.util.Random;
31 import org.apache.lucene.analysis.MockAnalyzer;
32 import org.apache.lucene.document.Document;
33 import org.apache.lucene.document.Field.Index;
34 import org.apache.lucene.document.Field.Store;
35 import org.apache.lucene.document.Field;
36 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
37 import org.apache.lucene.search.FieldCache;
38 import org.apache.lucene.search.IndexSearcher;
39 import org.apache.lucene.search.ScoreDoc;
40 import org.apache.lucene.search.TermQuery;
41 import org.apache.lucene.store.AlreadyClosedException;
42 import org.apache.lucene.store.Directory;
43 import org.apache.lucene.util.BitVector;
44 import org.apache.lucene.util.LuceneTestCase;
45 import org.apache.lucene.util._TestUtil;
47 public class TestIndexReaderReopen extends LuceneTestCase {
49 public void testReopen() throws Exception {
50 final Directory dir1 = newDirectory();
52 createIndex(random, dir1, false);
53 performDefaultTests(new TestReopen() {
56 protected void modifyIndex(int i) throws IOException {
57 TestIndexReaderReopen.modifyIndex(i, dir1);
61 protected IndexReader openReader() throws IOException {
62 return IndexReader.open(dir1, false);
68 final Directory dir2 = newDirectory();
70 createIndex(random, dir2, true);
71 performDefaultTests(new TestReopen() {
74 protected void modifyIndex(int i) throws IOException {
75 TestIndexReaderReopen.modifyIndex(i, dir2);
79 protected IndexReader openReader() throws IOException {
80 return IndexReader.open(dir2, false);
87 public void testParallelReaderReopen() throws Exception {
88 final Directory dir1 = newDirectory();
89 createIndex(random, dir1, true);
90 final Directory dir2 = newDirectory();
91 createIndex(random, dir2, true);
93 performDefaultTests(new TestReopen() {
96 protected void modifyIndex(int i) throws IOException {
97 TestIndexReaderReopen.modifyIndex(i, dir1);
98 TestIndexReaderReopen.modifyIndex(i, dir2);
102 protected IndexReader openReader() throws IOException {
103 ParallelReader pr = new ParallelReader();
104 pr.add(IndexReader.open(dir1, false));
105 pr.add(IndexReader.open(dir2, false));
113 final Directory dir3 = newDirectory();
114 createIndex(random, dir3, true);
115 final Directory dir4 = newDirectory();
116 createIndex(random, dir4, true);
118 performTestsWithExceptionInReopen(new TestReopen() {
121 protected void modifyIndex(int i) throws IOException {
122 TestIndexReaderReopen.modifyIndex(i, dir3);
123 TestIndexReaderReopen.modifyIndex(i, dir4);
127 protected IndexReader openReader() throws IOException {
128 ParallelReader pr = new ParallelReader();
129 pr.add(IndexReader.open(dir3, false));
130 pr.add(IndexReader.open(dir4, false));
131 // Does not implement reopen, so
133 pr.add(new FilterIndexReader(IndexReader.open(dir3, false)));
142 // LUCENE-1228: IndexWriter.commit() does not update the index version
143 // populate an index in iterations.
144 // at the end of every iteration, commit the index and reopen/recreate the reader.
145 // in each iteration verify the work of previous iteration.
146 // try this once with reopen once recreate, on both RAMDir and FSDir.
147 public void testCommitReopen () throws IOException {
148 Directory dir = newDirectory();
149 doTestReopenWithCommit(random, dir, true);
152 public void testCommitRecreate () throws IOException {
153 Directory dir = newDirectory();
154 doTestReopenWithCommit(random, dir, false);
158 private void doTestReopenWithCommit (Random random, Directory dir, boolean withReopen) throws IOException {
159 IndexWriter iwriter = new IndexWriter(dir, newIndexWriterConfig(
160 TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(
161 OpenMode.CREATE).setMergeScheduler(new SerialMergeScheduler()).setMergePolicy(newLogMergePolicy()));
163 IndexReader reader = IndexReader.open(dir, false);
166 for (int i=0; i<4; i++) {
167 for (int j=0; j<M; j++) {
168 Document doc = new Document();
169 doc.add(newField("id", i+"_"+j, Store.YES, Index.NOT_ANALYZED));
170 doc.add(newField("id2", i+"_"+j, Store.YES, Index.NOT_ANALYZED_NO_NORMS));
171 doc.add(newField("id3", i+"_"+j, Store.YES, Index.NO));
172 iwriter.addDocument(doc);
176 Document prevItereationDoc = reader.document(n);
177 assertNotNull(prevItereationDoc);
178 String id = prevItereationDoc.get("id");
179 assertEquals(k+"_"+j, id);
185 IndexReader r2 = IndexReader.openIfChanged(reader);
193 reader = IndexReader.open(dir, false);
202 public void testMultiReaderReopen() throws Exception {
203 final Directory dir1 = newDirectory();
204 createIndex(random, dir1, true);
206 final Directory dir2 = newDirectory();
207 createIndex(random, dir2, true);
209 performDefaultTests(new TestReopen() {
212 protected void modifyIndex(int i) throws IOException {
213 TestIndexReaderReopen.modifyIndex(i, dir1);
214 TestIndexReaderReopen.modifyIndex(i, dir2);
218 protected IndexReader openReader() throws IOException {
219 return new MultiReader(new IndexReader[]
220 {IndexReader.open(dir1, false),
221 IndexReader.open(dir2, false)});
229 final Directory dir3 = newDirectory();
230 createIndex(random, dir3, true);
232 final Directory dir4 = newDirectory();
233 createIndex(random, dir4, true);
235 performTestsWithExceptionInReopen(new TestReopen() {
238 protected void modifyIndex(int i) throws IOException {
239 TestIndexReaderReopen.modifyIndex(i, dir3);
240 TestIndexReaderReopen.modifyIndex(i, dir4);
244 protected IndexReader openReader() throws IOException {
245 return new MultiReader(new IndexReader[]
246 {IndexReader.open(dir3, false),
247 IndexReader.open(dir4, false),
248 // Does not implement reopen, so
250 new FilterIndexReader(IndexReader.open(dir3, false))});
258 public void testMixedReaders() throws Exception {
259 final Directory dir1 = newDirectory();
260 createIndex(random, dir1, true);
261 final Directory dir2 = newDirectory();
262 createIndex(random, dir2, true);
263 final Directory dir3 = newDirectory();
264 createIndex(random, dir3, false);
265 final Directory dir4 = newDirectory();
266 createIndex(random, dir4, true);
267 final Directory dir5 = newDirectory();
268 createIndex(random, dir5, false);
270 performDefaultTests(new TestReopen() {
273 protected void modifyIndex(int i) throws IOException {
274 // only change norms in this index to maintain the same number of docs for each of ParallelReader's subreaders
275 if (i == 1) TestIndexReaderReopen.modifyIndex(i, dir1);
277 TestIndexReaderReopen.modifyIndex(i, dir4);
278 TestIndexReaderReopen.modifyIndex(i, dir5);
282 protected IndexReader openReader() throws IOException {
283 ParallelReader pr = new ParallelReader();
284 pr.add(IndexReader.open(dir1, false));
285 pr.add(IndexReader.open(dir2, false));
286 MultiReader mr = new MultiReader(new IndexReader[] {
287 IndexReader.open(dir3, false), IndexReader.open(dir4, false)});
288 return new MultiReader(new IndexReader[] {
289 pr, mr, IndexReader.open(dir5, false)});
299 private void performDefaultTests(TestReopen test) throws Exception {
301 IndexReader index1 = test.openReader();
302 IndexReader index2 = test.openReader();
304 TestIndexReader.assertIndexEquals(index1, index2);
306 // verify that reopen() does not return a new reader instance
307 // in case the index has no changes
308 ReaderCouple couple = refreshReader(index2, false);
309 assertTrue(couple.refreshedReader == index2);
311 couple = refreshReader(index2, test, 0, true);
313 index1 = couple.newReader;
315 IndexReader index2_refreshed = couple.refreshedReader;
318 // test if refreshed reader and newly opened reader return equal results
319 TestIndexReader.assertIndexEquals(index1, index2_refreshed);
321 index2_refreshed.close();
322 assertReaderClosed(index2, true, true);
323 assertReaderClosed(index2_refreshed, true, true);
325 index2 = test.openReader();
327 for (int i = 1; i < 4; i++) {
330 couple = refreshReader(index2, test, i, true);
331 // refresh IndexReader
334 index2 = couple.refreshedReader;
335 index1 = couple.newReader;
336 TestIndexReader.assertIndexEquals(index1, index2);
341 assertReaderClosed(index1, true, true);
342 assertReaderClosed(index2, true, true);
345 public void testReferenceCounting() throws IOException {
346 for (int mode = 0; mode < 4; mode++) {
347 Directory dir1 = newDirectory();
348 createIndex(random, dir1, true);
350 IndexReader reader0 = IndexReader.open(dir1, false);
351 assertRefCountEquals(1, reader0);
353 assertTrue(reader0 instanceof DirectoryReader);
354 IndexReader[] subReaders0 = reader0.getSequentialSubReaders();
355 for (int i = 0; i < subReaders0.length; i++) {
356 assertRefCountEquals(1, subReaders0[i]);
359 // delete first document, so that only one of the subReaders have to be re-opened
360 IndexReader modifier = IndexReader.open(dir1, false);
361 modifier.deleteDocument(0);
364 IndexReader reader1 = refreshReader(reader0, true).refreshedReader;
365 assertTrue(reader1 instanceof DirectoryReader);
366 IndexReader[] subReaders1 = reader1.getSequentialSubReaders();
367 assertEquals(subReaders0.length, subReaders1.length);
369 for (int i = 0; i < subReaders0.length; i++) {
370 if (subReaders0[i] != subReaders1[i]) {
371 assertRefCountEquals(1, subReaders0[i]);
372 assertRefCountEquals(1, subReaders1[i]);
374 assertRefCountEquals(2, subReaders0[i]);
378 // delete first document, so that only one of the subReaders have to be re-opened
379 modifier = IndexReader.open(dir1, false);
380 modifier.deleteDocument(1);
383 IndexReader reader2 = refreshReader(reader1, true).refreshedReader;
384 assertTrue(reader2 instanceof DirectoryReader);
385 IndexReader[] subReaders2 = reader2.getSequentialSubReaders();
386 assertEquals(subReaders1.length, subReaders2.length);
388 for (int i = 0; i < subReaders2.length; i++) {
389 if (subReaders2[i] == subReaders1[i]) {
390 if (subReaders1[i] == subReaders0[i]) {
391 assertRefCountEquals(3, subReaders2[i]);
393 assertRefCountEquals(2, subReaders2[i]);
396 assertRefCountEquals(1, subReaders2[i]);
397 if (subReaders0[i] == subReaders1[i]) {
398 assertRefCountEquals(2, subReaders2[i]);
399 assertRefCountEquals(2, subReaders0[i]);
401 assertRefCountEquals(1, subReaders0[i]);
402 assertRefCountEquals(1, subReaders1[i]);
407 IndexReader reader3 = refreshReader(reader0, true).refreshedReader;
408 assertTrue(reader3 instanceof DirectoryReader);
409 IndexReader[] subReaders3 = reader3.getSequentialSubReaders();
410 assertEquals(subReaders3.length, subReaders0.length);
412 // try some permutations
440 assertReaderClosed(reader0, true, true);
441 assertReaderClosed(reader1, true, true);
442 assertReaderClosed(reader2, true, true);
443 assertReaderClosed(reader3, true, true);
450 public void testReferenceCountingMultiReader() throws IOException {
451 for (int mode = 0; mode <=1; mode++) {
452 Directory dir1 = newDirectory();
453 createIndex(random, dir1, false);
454 Directory dir2 = newDirectory();
455 createIndex(random, dir2, true);
457 IndexReader reader1 = IndexReader.open(dir1, false);
458 assertRefCountEquals(1, reader1);
460 IndexReader initReader2 = IndexReader.open(dir2, false);
461 IndexReader multiReader1 = new MultiReader(new IndexReader[] {reader1, initReader2}, (mode == 0));
462 modifyIndex(0, dir2);
463 assertRefCountEquals(1 + mode, reader1);
465 IndexReader multiReader2 = IndexReader.openIfChanged(multiReader1);
466 assertNotNull(multiReader2);
467 // index1 hasn't changed, so multiReader2 should share reader1 now with multiReader1
468 assertRefCountEquals(2 + mode, reader1);
470 modifyIndex(0, dir1);
471 IndexReader reader2 = IndexReader.openIfChanged(reader1);
472 assertNotNull(reader2);
473 assertNull(IndexReader.openIfChanged(reader2));
474 assertRefCountEquals(2 + mode, reader1);
480 modifyIndex(1, dir1);
481 IndexReader reader3 = IndexReader.openIfChanged(reader2);
482 assertNotNull(reader3);
483 assertRefCountEquals(2 + mode, reader1);
484 assertRefCountEquals(1, reader2);
486 multiReader1.close();
487 assertRefCountEquals(1 + mode, reader1);
489 multiReader1.close();
490 assertRefCountEquals(1 + mode, reader1);
497 assertRefCountEquals(1, reader1);
499 multiReader2.close();
500 assertRefCountEquals(0, reader1);
502 multiReader2.close();
503 assertRefCountEquals(0, reader1);
506 assertRefCountEquals(0, reader1);
507 assertReaderClosed(reader1, true, false);
510 assertRefCountEquals(0, reader1);
511 assertReaderClosed(reader1, true, false);
514 assertRefCountEquals(0, reader1);
517 assertRefCountEquals(0, reader1);
518 assertReaderClosed(reader1, true, true);
525 public void testReferenceCountingParallelReader() throws IOException {
526 for (int mode = 0; mode <=1; mode++) {
527 Directory dir1 = newDirectory();
528 createIndex(random, dir1, false);
529 Directory dir2 = newDirectory();
530 createIndex(random, dir2, true);
532 IndexReader reader1 = IndexReader.open(dir1, false);
533 assertRefCountEquals(1, reader1);
535 ParallelReader parallelReader1 = new ParallelReader(mode == 0);
536 parallelReader1.add(reader1);
537 IndexReader initReader2 = IndexReader.open(dir2, false);
538 parallelReader1.add(initReader2);
539 modifyIndex(1, dir2);
540 assertRefCountEquals(1 + mode, reader1);
542 IndexReader parallelReader2 = IndexReader.openIfChanged(parallelReader1);
543 assertNotNull(parallelReader2);
544 assertNull(IndexReader.openIfChanged(parallelReader2));
545 // index1 hasn't changed, so parallelReader2 should share reader1 now with multiReader1
546 assertRefCountEquals(2 + mode, reader1);
548 modifyIndex(0, dir1);
549 modifyIndex(0, dir2);
550 IndexReader reader2 = IndexReader.openIfChanged(reader1);
551 assertNotNull(reader2);
552 assertRefCountEquals(2 + mode, reader1);
558 modifyIndex(4, dir1);
559 IndexReader reader3 = IndexReader.openIfChanged(reader2);
560 assertNotNull(reader3);
561 assertRefCountEquals(2 + mode, reader1);
562 assertRefCountEquals(1, reader2);
564 parallelReader1.close();
565 assertRefCountEquals(1 + mode, reader1);
567 parallelReader1.close();
568 assertRefCountEquals(1 + mode, reader1);
575 assertRefCountEquals(1, reader1);
577 parallelReader2.close();
578 assertRefCountEquals(0, reader1);
580 parallelReader2.close();
581 assertRefCountEquals(0, reader1);
584 assertRefCountEquals(0, reader1);
585 assertReaderClosed(reader1, true, false);
588 assertRefCountEquals(0, reader1);
589 assertReaderClosed(reader1, true, false);
592 assertRefCountEquals(0, reader1);
595 assertRefCountEquals(0, reader1);
596 assertReaderClosed(reader1, true, true);
604 public void testNormsRefCounting() throws IOException {
605 Directory dir1 = newDirectory();
606 createIndex(random, dir1, false);
608 IndexReader reader1 = IndexReader.open(dir1, false);
609 SegmentReader segmentReader1 = SegmentReader.getOnlySegmentReader(reader1);
610 IndexReader modifier = IndexReader.open(dir1, false);
611 modifier.deleteDocument(0);
614 IndexReader reader2 = IndexReader.openIfChanged(reader1);
615 assertNotNull(reader2);
616 modifier = IndexReader.open(dir1, false);
617 modifier.setNorm(1, "field1", 50);
618 modifier.setNorm(1, "field2", 50);
621 IndexReader reader3 = IndexReader.openIfChanged(reader2);
622 assertNotNull(reader3);
623 SegmentReader segmentReader3 = SegmentReader.getOnlySegmentReader(reader3);
624 modifier = IndexReader.open(dir1, false);
625 modifier.deleteDocument(2);
628 IndexReader reader4 = IndexReader.openIfChanged(reader3);
629 assertNotNull(reader4);
630 modifier = IndexReader.open(dir1, false);
631 modifier.deleteDocument(3);
634 IndexReader reader5 = IndexReader.openIfChanged(reader3);
635 assertNotNull(reader5);
637 // Now reader2-reader5 references reader1. reader1 and reader2
638 // share the same norms. reader3, reader4, reader5 also share norms.
639 assertRefCountEquals(1, reader1);
640 assertFalse(segmentReader1.normsClosed());
644 assertRefCountEquals(0, reader1);
645 assertFalse(segmentReader1.normsClosed());
648 assertRefCountEquals(0, reader1);
650 // now the norms for field1 and field2 should be closed
651 assertTrue(segmentReader1.normsClosed("field1"));
652 assertTrue(segmentReader1.normsClosed("field2"));
654 // but the norms for field3 and field4 should still be open
655 assertFalse(segmentReader1.normsClosed("field3"));
656 assertFalse(segmentReader1.normsClosed("field4"));
659 assertRefCountEquals(0, reader1);
660 assertFalse(segmentReader3.normsClosed());
662 assertRefCountEquals(0, reader1);
663 assertFalse(segmentReader3.normsClosed());
665 assertRefCountEquals(0, reader1);
667 // and now all norms that reader1 used should be closed
668 assertTrue(segmentReader1.normsClosed());
670 // now that reader3, reader4 and reader5 are closed,
671 // the norms that those three readers shared should be
673 assertTrue(segmentReader3.normsClosed());
678 private void performTestsWithExceptionInReopen(TestReopen test) throws Exception {
679 IndexReader index1 = test.openReader();
680 IndexReader index2 = test.openReader();
682 TestIndexReader.assertIndexEquals(index1, index2);
685 refreshReader(index1, test, 0, true);
686 fail("Expected exception not thrown.");
687 } catch (Exception e) {
688 // expected exception
691 // index2 should still be usable and unaffected by the failed reopen() call
692 TestIndexReader.assertIndexEquals(index1, index2);
698 public void testThreadSafety() throws Exception {
699 final Directory dir = newDirectory();
700 // NOTE: this also controls the number of threads!
701 final int n = _TestUtil.nextInt(random, 20, 40);
702 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
703 TEST_VERSION_CURRENT, new MockAnalyzer(random)));
704 for (int i = 0; i < n; i++) {
705 writer.addDocument(createDocument(i, 3));
707 writer.forceMerge(1);
710 final TestReopen test = new TestReopen() {
712 protected void modifyIndex(int i) throws IOException {
714 IndexReader modifier = IndexReader.open(dir, false);
715 modifier.setNorm(i, "field1", 50);
717 } else if (i % 3 == 1) {
718 IndexReader modifier = IndexReader.open(dir, false);
719 modifier.deleteDocument(i % modifier.maxDoc());
722 IndexWriter modifier = new IndexWriter(dir, new IndexWriterConfig(
723 TEST_VERSION_CURRENT, new MockAnalyzer(random)));
724 modifier.addDocument(createDocument(n + i, 6));
730 protected IndexReader openReader() throws IOException {
731 return IndexReader.open(dir, false);
735 final List<ReaderCouple> readers = Collections.synchronizedList(new ArrayList<ReaderCouple>());
736 IndexReader firstReader = IndexReader.open(dir, false);
737 IndexReader reader = firstReader;
738 final Random rnd = random;
740 ReaderThread[] threads = new ReaderThread[n];
741 final Set<IndexReader> readersToClose = Collections.synchronizedSet(new HashSet<IndexReader>());
743 for (int i = 0; i < n; i++) {
745 IndexReader refreshed = IndexReader.openIfChanged(reader);
746 if (refreshed != null) {
747 readersToClose.add(reader);
751 final IndexReader r = reader;
755 ReaderThreadTask task;
757 if (i < 4 || (i >=10 && i < 14) || i > 18) {
758 task = new ReaderThreadTask() {
761 public void run() throws Exception {
763 if (index % 2 == 0) {
764 // refresh reader synchronized
765 ReaderCouple c = (refreshReader(r, test, index, true));
766 readersToClose.add(c.newReader);
767 readersToClose.add(c.refreshedReader);
769 // prevent too many readers
773 IndexReader refreshed = IndexReader.openIfChanged(r);
774 if (refreshed == null) {
778 IndexSearcher searcher = newSearcher(refreshed);
779 ScoreDoc[] hits = searcher.search(
780 new TermQuery(new Term("field1", "a" + rnd.nextInt(refreshed.maxDoc()))),
781 null, 1000).scoreDocs;
782 if (hits.length > 0) {
783 searcher.doc(hits[0].doc);
786 if (refreshed != r) {
791 wait(_TestUtil.nextInt(random, 1, 100));
798 task = new ReaderThreadTask() {
800 public void run() throws Exception {
802 int numReaders = readers.size();
803 if (numReaders > 0) {
804 ReaderCouple c = readers.get(rnd.nextInt(numReaders));
805 TestIndexReader.assertIndexEquals(c.newReader, c.refreshedReader);
809 wait(_TestUtil.nextInt(random, 1, 100));
816 threads[i] = new ReaderThread(task);
824 for (int i = 0; i < n; i++) {
825 if (threads[i] != null) {
826 threads[i].stopThread();
830 for (int i = 0; i < n; i++) {
831 if (threads[i] != null) {
833 if (threads[i].error != null) {
834 String msg = "Error occurred in thread " + threads[i].getName() + ":\n" + threads[i].error.getMessage();
841 for (final IndexReader readerToClose : readersToClose) {
842 readerToClose.close();
848 for (final IndexReader readerToClose : readersToClose) {
849 assertReaderClosed(readerToClose, true, true);
852 assertReaderClosed(reader, true, true);
853 assertReaderClosed(firstReader, true, true);
858 private static class ReaderCouple {
859 ReaderCouple(IndexReader r1, IndexReader r2) {
861 refreshedReader = r2;
864 IndexReader newReader;
865 IndexReader refreshedReader;
868 private abstract static class ReaderThreadTask {
869 protected volatile boolean stopped;
874 public abstract void run() throws Exception;
877 private static class ReaderThread extends Thread {
878 private ReaderThreadTask task;
879 private Throwable error;
882 ReaderThread(ReaderThreadTask task) {
886 public void stopThread() {
894 } catch (Throwable r) {
895 r.printStackTrace(System.out);
901 private Object createReaderMutex = new Object();
903 private ReaderCouple refreshReader(IndexReader reader, boolean hasChanges) throws IOException {
904 return refreshReader(reader, null, -1, hasChanges);
907 ReaderCouple refreshReader(IndexReader reader, TestReopen test, int modify, boolean hasChanges) throws IOException {
908 synchronized (createReaderMutex) {
909 IndexReader r = null;
911 test.modifyIndex(modify);
912 r = test.openReader();
915 IndexReader refreshed = null;
917 refreshed = IndexReader.openIfChanged(reader);
918 if (refreshed == null) {
922 if (refreshed == null && r != null) {
923 // Hit exception -- close opened reader
929 if (refreshed == reader) {
930 fail("No new IndexReader instance created during refresh.");
933 if (refreshed != reader) {
934 fail("New IndexReader instance created during refresh even though index had no changes.");
938 return new ReaderCouple(r, refreshed);
942 public static void createIndex(Random random, Directory dir, boolean multiSegment) throws IOException {
943 IndexWriter.unlock(dir);
944 IndexWriter w = new IndexWriter(dir, LuceneTestCase.newIndexWriterConfig(random,
945 TEST_VERSION_CURRENT, new MockAnalyzer(random))
946 .setMergePolicy(new LogDocMergePolicy()));
948 for (int i = 0; i < 100; i++) {
949 w.addDocument(createDocument(i, 4));
950 if (multiSegment && (i % 10) == 0) {
961 IndexReader r = IndexReader.open(dir, false);
963 assertTrue(r.getSequentialSubReaders().length > 1);
965 assertTrue(r.getSequentialSubReaders().length == 1);
970 public static Document createDocument(int n, int numFields) {
971 StringBuilder sb = new StringBuilder();
972 Document doc = new Document();
975 doc.add(new Field("field1", sb.toString(), Store.YES, Index.ANALYZED));
976 doc.add(new Field("fielda", sb.toString(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));
977 doc.add(new Field("fieldb", sb.toString(), Store.YES, Index.NO));
980 for (int i = 1; i < numFields; i++) {
981 doc.add(new Field("field" + (i+1), sb.toString(), Store.YES, Index.ANALYZED));
986 static void modifyIndex(int i, Directory dir) throws IOException {
990 System.out.println("TEST: modify index");
992 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
993 w.setInfoStream(VERBOSE ? System.out : null);
994 w.deleteDocuments(new Term("field2", "a11"));
995 w.deleteDocuments(new Term("field2", "b30"));
1000 IndexReader reader = IndexReader.open(dir, false);
1001 reader.setNorm(4, "field1", 123);
1002 reader.setNorm(44, "field2", 222);
1003 reader.setNorm(44, "field4", 22);
1008 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
1014 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
1015 w.addDocument(createDocument(101, 4));
1017 w.addDocument(createDocument(102, 4));
1018 w.addDocument(createDocument(103, 4));
1023 IndexReader reader = IndexReader.open(dir, false);
1024 reader.setNorm(5, "field1", 123);
1025 reader.setNorm(55, "field2", 222);
1030 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
1031 w.addDocument(createDocument(101, 4));
1038 private void assertReaderClosed(IndexReader reader, boolean checkSubReaders, boolean checkNormsClosed) {
1039 assertEquals(0, reader.getRefCount());
1041 if (checkNormsClosed && reader instanceof SegmentReader) {
1042 assertTrue(((SegmentReader) reader).normsClosed());
1045 if (checkSubReaders) {
1046 if (reader instanceof DirectoryReader) {
1047 IndexReader[] subReaders = reader.getSequentialSubReaders();
1048 for (int i = 0; i < subReaders.length; i++) {
1049 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1053 if (reader instanceof MultiReader) {
1054 IndexReader[] subReaders = reader.getSequentialSubReaders();
1055 for (int i = 0; i < subReaders.length; i++) {
1056 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1060 if (reader instanceof ParallelReader) {
1061 IndexReader[] subReaders = ((ParallelReader) reader).getSubReaders();
1062 for (int i = 0; i < subReaders.length; i++) {
1063 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1070 private void assertReaderOpen(IndexReader reader) {
1071 reader.ensureOpen();
1073 if (reader instanceof DirectoryReader) {
1074 IndexReader[] subReaders = reader.getSequentialSubReaders();
1075 for (int i = 0; i < subReaders.length; i++) {
1076 assertReaderOpen(subReaders[i]);
1082 private void assertRefCountEquals(int refCount, IndexReader reader) {
1083 assertEquals("Reader has wrong refCount value.", refCount, reader.getRefCount());
1087 private abstract static class TestReopen {
1088 protected abstract IndexReader openReader() throws IOException;
1089 protected abstract void modifyIndex(int i) throws IOException;
1092 public void testCloseOrig() throws Throwable {
1093 Directory dir = newDirectory();
1094 createIndex(random, dir, false);
1095 IndexReader r1 = IndexReader.open(dir, false);
1096 IndexReader r2 = IndexReader.open(dir, false);
1097 r2.deleteDocument(0);
1100 IndexReader r3 = IndexReader.openIfChanged(r1);
1102 assertTrue(r1 != r3);
1106 fail("did not hit exception");
1107 } catch (AlreadyClosedException ace) {
1114 public void testDeletes() throws Throwable {
1115 Directory dir = newDirectory();
1116 createIndex(random, dir, false); // Create an index with a bunch of docs (1 segment)
1118 modifyIndex(0, dir); // Get delete bitVector on 1st segment
1119 modifyIndex(5, dir); // Add a doc (2 segments)
1121 IndexReader r1 = IndexReader.open(dir, false); // MSR
1123 modifyIndex(5, dir); // Add another doc (3 segments)
1125 IndexReader r2 = IndexReader.openIfChanged(r1); // MSR
1127 assertNull(IndexReader.openIfChanged(r2));
1128 assertTrue(r1 != r2);
1130 SegmentReader sr1 = (SegmentReader) r1.getSequentialSubReaders()[0]; // Get SRs for the first segment from original
1131 SegmentReader sr2 = (SegmentReader) r2.getSequentialSubReaders()[0]; // and reopened IRs
1133 // At this point they share the same BitVector
1134 assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1136 r2.deleteDocument(0);
1138 // r1 should not see the delete
1139 assertFalse(r1.isDeleted(0));
1141 // Now r2 should have made a private copy of deleted docs:
1142 assertTrue(sr1.deletedDocs!=sr2.deletedDocs);
1149 public void testDeletes2() throws Throwable {
1150 Directory dir = newDirectory();
1151 createIndex(random, dir, false);
1152 // Get delete bitVector
1153 modifyIndex(0, dir);
1154 IndexReader r1 = IndexReader.open(dir, false);
1157 modifyIndex(5, dir);
1159 IndexReader r2 = IndexReader.openIfChanged(r1);
1161 assertTrue(r1 != r2);
1163 IndexReader[] rs2 = r2.getSequentialSubReaders();
1165 SegmentReader sr1 = SegmentReader.getOnlySegmentReader(r1);
1166 SegmentReader sr2 = (SegmentReader) rs2[0];
1168 // At this point they share the same BitVector
1169 assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1170 final BitVector delDocs = sr1.deletedDocs;
1173 r2.deleteDocument(0);
1174 assertTrue(delDocs==sr2.deletedDocs);
1179 private static class KeepAllCommits implements IndexDeletionPolicy {
1180 public void onInit(List<? extends IndexCommit> commits) {
1182 public void onCommit(List<? extends IndexCommit> commits) {
1186 public void testReopenOnCommit() throws Throwable {
1187 Directory dir = newDirectory();
1188 IndexWriter writer = new IndexWriter(
1190 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1191 setIndexDeletionPolicy(new KeepAllCommits()).
1192 setMaxBufferedDocs(-1).
1193 setMergePolicy(newLogMergePolicy(10))
1195 for(int i=0;i<4;i++) {
1196 Document doc = new Document();
1197 doc.add(newField("id", ""+i, Field.Store.NO, Field.Index.NOT_ANALYZED));
1198 writer.addDocument(doc);
1199 Map<String,String> data = new HashMap<String,String>();
1200 data.put("index", i+"");
1201 writer.commit(data);
1203 for(int i=0;i<4;i++) {
1204 writer.deleteDocuments(new Term("id", ""+i));
1205 Map<String,String> data = new HashMap<String,String>();
1206 data.put("index", (4+i)+"");
1207 writer.commit(data);
1211 IndexReader r = IndexReader.open(dir, false);
1212 assertEquals(0, r.numDocs());
1214 Collection<IndexCommit> commits = IndexReader.listCommits(dir);
1215 for (final IndexCommit commit : commits) {
1216 IndexReader r2 = IndexReader.openIfChanged(r, commit);
1218 assertTrue(r2 != r);
1220 // Reader should be readOnly
1222 r2.deleteDocument(0);
1223 fail("no exception hit");
1224 } catch (UnsupportedOperationException uoe) {
1228 final Map<String,String> s = commit.getUserData();
1230 if (s.size() == 0) {
1231 // First commit created by IW
1234 v = Integer.parseInt(s.get("index"));
1237 assertEquals(1+v, r2.numDocs());
1239 assertEquals(7-v, r2.numDocs());
1248 // LUCENE-1579: Make sure all SegmentReaders are new when
1249 // reopen switches readOnly
1250 public void testReopenChangeReadonly() throws Exception {
1251 Directory dir = newDirectory();
1252 IndexWriter writer = new IndexWriter(
1254 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1255 setMaxBufferedDocs(-1).
1256 setMergePolicy(newLogMergePolicy(10))
1258 Document doc = new Document();
1259 doc.add(newField("number", "17", Field.Store.NO, Field.Index.NOT_ANALYZED));
1260 writer.addDocument(doc);
1264 IndexReader r = IndexReader.open(dir, false);
1265 assertTrue(r instanceof DirectoryReader);
1266 IndexReader r1 = SegmentReader.getOnlySegmentReader(r);
1267 final int[] ints = FieldCache.DEFAULT.getInts(r1, "number");
1268 assertEquals(1, ints.length);
1269 assertEquals(17, ints[0]);
1271 // Reopen to readonly w/ no chnages
1272 IndexReader r3 = IndexReader.openIfChanged(r, true);
1274 assertTrue(r3 instanceof ReadOnlyDirectoryReader);
1278 writer.addDocument(doc);
1281 // Reopen reader1 --> reader2
1282 IndexReader r2 = IndexReader.openIfChanged(r, true);
1285 assertTrue(r2 instanceof ReadOnlyDirectoryReader);
1286 IndexReader[] subs = r2.getSequentialSubReaders();
1287 final int[] ints2 = FieldCache.DEFAULT.getInts(subs[0], "number");
1290 assertTrue(subs[0] instanceof ReadOnlySegmentReader);
1291 assertTrue(subs[1] instanceof ReadOnlySegmentReader);
1292 assertTrue(ints == ints2);