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.
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
29 import java.util.Random;
32 import org.apache.lucene.analysis.MockAnalyzer;
33 import org.apache.lucene.document.Document;
34 import org.apache.lucene.document.Field.Index;
35 import org.apache.lucene.document.Field.Store;
36 import org.apache.lucene.document.Field;
37 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
38 import org.apache.lucene.search.FieldCache;
39 import org.apache.lucene.search.IndexSearcher;
40 import org.apache.lucene.search.ScoreDoc;
41 import org.apache.lucene.search.TermQuery;
42 import org.apache.lucene.store.AlreadyClosedException;
43 import org.apache.lucene.store.Directory;
44 import org.apache.lucene.util.BitVector;
45 import org.apache.lucene.util.LuceneTestCase;
46 import org.apache.lucene.util._TestUtil;
48 public class TestIndexReaderReopen extends LuceneTestCase {
50 public void testReopen() throws Exception {
51 final Directory dir1 = newDirectory();
53 createIndex(random, dir1, false);
54 performDefaultTests(new TestReopen() {
57 protected void modifyIndex(int i) throws IOException {
58 TestIndexReaderReopen.modifyIndex(i, dir1);
62 protected IndexReader openReader() throws IOException {
63 return IndexReader.open(dir1, false);
69 final Directory dir2 = newDirectory();
71 createIndex(random, dir2, true);
72 performDefaultTests(new TestReopen() {
75 protected void modifyIndex(int i) throws IOException {
76 TestIndexReaderReopen.modifyIndex(i, dir2);
80 protected IndexReader openReader() throws IOException {
81 return IndexReader.open(dir2, false);
88 public void testParallelReaderReopen() throws Exception {
89 final Directory dir1 = newDirectory();
90 createIndex(random, dir1, true);
91 final Directory dir2 = newDirectory();
92 createIndex(random, dir2, true);
94 performDefaultTests(new TestReopen() {
97 protected void modifyIndex(int i) throws IOException {
98 TestIndexReaderReopen.modifyIndex(i, dir1);
99 TestIndexReaderReopen.modifyIndex(i, dir2);
103 protected IndexReader openReader() throws IOException {
104 ParallelReader pr = new ParallelReader();
105 pr.add(IndexReader.open(dir1, false));
106 pr.add(IndexReader.open(dir2, false));
114 final Directory dir3 = newDirectory();
115 createIndex(random, dir3, true);
116 final Directory dir4 = newDirectory();
117 createIndex(random, dir4, true);
119 performTestsWithExceptionInReopen(new TestReopen() {
122 protected void modifyIndex(int i) throws IOException {
123 TestIndexReaderReopen.modifyIndex(i, dir3);
124 TestIndexReaderReopen.modifyIndex(i, dir4);
128 protected IndexReader openReader() throws IOException {
129 ParallelReader pr = new ParallelReader();
130 pr.add(IndexReader.open(dir3, false));
131 pr.add(IndexReader.open(dir4, false));
132 // Does not implement reopen, so
134 pr.add(new FilterIndexReader(IndexReader.open(dir3, false)));
143 // LUCENE-1228: IndexWriter.commit() does not update the index version
144 // populate an index in iterations.
145 // at the end of every iteration, commit the index and reopen/recreate the reader.
146 // in each iteration verify the work of previous iteration.
147 // try this once with reopen once recreate, on both RAMDir and FSDir.
148 public void testCommitReopen () throws IOException {
149 Directory dir = newDirectory();
150 doTestReopenWithCommit(random, dir, true);
153 public void testCommitRecreate () throws IOException {
154 Directory dir = newDirectory();
155 doTestReopenWithCommit(random, dir, false);
159 private void doTestReopenWithCommit (Random random, Directory dir, boolean withReopen) throws IOException {
160 IndexWriter iwriter = new IndexWriter(dir, newIndexWriterConfig(
161 TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(
162 OpenMode.CREATE).setMergeScheduler(new SerialMergeScheduler()).setMergePolicy(newLogMergePolicy()));
164 IndexReader reader = IndexReader.open(dir, false);
167 for (int i=0; i<4; i++) {
168 for (int j=0; j<M; j++) {
169 Document doc = new Document();
170 doc.add(newField("id", i+"_"+j, Store.YES, Index.NOT_ANALYZED));
171 doc.add(newField("id2", i+"_"+j, Store.YES, Index.NOT_ANALYZED_NO_NORMS));
172 doc.add(newField("id3", i+"_"+j, Store.YES, Index.NO));
173 iwriter.addDocument(doc);
177 Document prevItereationDoc = reader.document(n);
178 assertNotNull(prevItereationDoc);
179 String id = prevItereationDoc.get("id");
180 assertEquals(k+"_"+j, id);
186 IndexReader r2 = reader.reopen();
194 reader = IndexReader.open(dir, false);
203 public void testMultiReaderReopen() throws Exception {
204 final Directory dir1 = newDirectory();
205 createIndex(random, dir1, true);
207 final Directory dir2 = newDirectory();
208 createIndex(random, dir2, true);
210 performDefaultTests(new TestReopen() {
213 protected void modifyIndex(int i) throws IOException {
214 TestIndexReaderReopen.modifyIndex(i, dir1);
215 TestIndexReaderReopen.modifyIndex(i, dir2);
219 protected IndexReader openReader() throws IOException {
220 return new MultiReader(new IndexReader[]
221 {IndexReader.open(dir1, false),
222 IndexReader.open(dir2, false)});
230 final Directory dir3 = newDirectory();
231 createIndex(random, dir3, true);
233 final Directory dir4 = newDirectory();
234 createIndex(random, dir4, true);
236 performTestsWithExceptionInReopen(new TestReopen() {
239 protected void modifyIndex(int i) throws IOException {
240 TestIndexReaderReopen.modifyIndex(i, dir3);
241 TestIndexReaderReopen.modifyIndex(i, dir4);
245 protected IndexReader openReader() throws IOException {
246 return new MultiReader(new IndexReader[]
247 {IndexReader.open(dir3, false),
248 IndexReader.open(dir4, false),
249 // Does not implement reopen, so
251 new FilterIndexReader(IndexReader.open(dir3, false))});
259 public void testMixedReaders() throws Exception {
260 final Directory dir1 = newDirectory();
261 createIndex(random, dir1, true);
262 final Directory dir2 = newDirectory();
263 createIndex(random, dir2, true);
264 final Directory dir3 = newDirectory();
265 createIndex(random, dir3, false);
266 final Directory dir4 = newDirectory();
267 createIndex(random, dir4, true);
268 final Directory dir5 = newDirectory();
269 createIndex(random, dir5, false);
271 performDefaultTests(new TestReopen() {
274 protected void modifyIndex(int i) throws IOException {
275 // only change norms in this index to maintain the same number of docs for each of ParallelReader's subreaders
276 if (i == 1) TestIndexReaderReopen.modifyIndex(i, dir1);
278 TestIndexReaderReopen.modifyIndex(i, dir4);
279 TestIndexReaderReopen.modifyIndex(i, dir5);
283 protected IndexReader openReader() throws IOException {
284 ParallelReader pr = new ParallelReader();
285 pr.add(IndexReader.open(dir1, false));
286 pr.add(IndexReader.open(dir2, false));
287 MultiReader mr = new MultiReader(new IndexReader[] {
288 IndexReader.open(dir3, false), IndexReader.open(dir4, false)});
289 return new MultiReader(new IndexReader[] {
290 pr, mr, IndexReader.open(dir5, false)});
300 private void performDefaultTests(TestReopen test) throws Exception {
302 IndexReader index1 = test.openReader();
303 IndexReader index2 = test.openReader();
305 TestIndexReader.assertIndexEquals(index1, index2);
307 // verify that reopen() does not return a new reader instance
308 // in case the index has no changes
309 ReaderCouple couple = refreshReader(index2, false);
310 assertTrue(couple.refreshedReader == index2);
312 couple = refreshReader(index2, test, 0, true);
314 index1 = couple.newReader;
316 IndexReader index2_refreshed = couple.refreshedReader;
319 // test if refreshed reader and newly opened reader return equal results
320 TestIndexReader.assertIndexEquals(index1, index2_refreshed);
322 index2_refreshed.close();
323 assertReaderClosed(index2, true, true);
324 assertReaderClosed(index2_refreshed, true, true);
326 index2 = test.openReader();
328 for (int i = 1; i < 4; i++) {
331 couple = refreshReader(index2, test, i, true);
332 // refresh IndexReader
335 index2 = couple.refreshedReader;
336 index1 = couple.newReader;
337 TestIndexReader.assertIndexEquals(index1, index2);
342 assertReaderClosed(index1, true, true);
343 assertReaderClosed(index2, true, true);
346 public void testReferenceCounting() throws IOException {
347 for (int mode = 0; mode < 4; mode++) {
348 Directory dir1 = newDirectory();
349 createIndex(random, dir1, true);
351 IndexReader reader0 = IndexReader.open(dir1, false);
352 assertRefCountEquals(1, reader0);
354 assertTrue(reader0 instanceof DirectoryReader);
355 IndexReader[] subReaders0 = reader0.getSequentialSubReaders();
356 for (int i = 0; i < subReaders0.length; i++) {
357 assertRefCountEquals(1, subReaders0[i]);
360 // delete first document, so that only one of the subReaders have to be re-opened
361 IndexReader modifier = IndexReader.open(dir1, false);
362 modifier.deleteDocument(0);
365 IndexReader reader1 = refreshReader(reader0, true).refreshedReader;
366 assertTrue(reader1 instanceof DirectoryReader);
367 IndexReader[] subReaders1 = reader1.getSequentialSubReaders();
368 assertEquals(subReaders0.length, subReaders1.length);
370 for (int i = 0; i < subReaders0.length; i++) {
371 if (subReaders0[i] != subReaders1[i]) {
372 assertRefCountEquals(1, subReaders0[i]);
373 assertRefCountEquals(1, subReaders1[i]);
375 assertRefCountEquals(2, subReaders0[i]);
379 // delete first document, so that only one of the subReaders have to be re-opened
380 modifier = IndexReader.open(dir1, false);
381 modifier.deleteDocument(1);
384 IndexReader reader2 = refreshReader(reader1, true).refreshedReader;
385 assertTrue(reader2 instanceof DirectoryReader);
386 IndexReader[] subReaders2 = reader2.getSequentialSubReaders();
387 assertEquals(subReaders1.length, subReaders2.length);
389 for (int i = 0; i < subReaders2.length; i++) {
390 if (subReaders2[i] == subReaders1[i]) {
391 if (subReaders1[i] == subReaders0[i]) {
392 assertRefCountEquals(3, subReaders2[i]);
394 assertRefCountEquals(2, subReaders2[i]);
397 assertRefCountEquals(1, subReaders2[i]);
398 if (subReaders0[i] == subReaders1[i]) {
399 assertRefCountEquals(2, subReaders2[i]);
400 assertRefCountEquals(2, subReaders0[i]);
402 assertRefCountEquals(1, subReaders0[i]);
403 assertRefCountEquals(1, subReaders1[i]);
408 IndexReader reader3 = refreshReader(reader0, true).refreshedReader;
409 assertTrue(reader3 instanceof DirectoryReader);
410 IndexReader[] subReaders3 = reader3.getSequentialSubReaders();
411 assertEquals(subReaders3.length, subReaders0.length);
413 // try some permutations
441 assertReaderClosed(reader0, true, true);
442 assertReaderClosed(reader1, true, true);
443 assertReaderClosed(reader2, true, true);
444 assertReaderClosed(reader3, true, true);
451 public void testReferenceCountingMultiReader() throws IOException {
452 for (int mode = 0; mode <=1; mode++) {
453 Directory dir1 = newDirectory();
454 createIndex(random, dir1, false);
455 Directory dir2 = newDirectory();
456 createIndex(random, dir2, true);
458 IndexReader reader1 = IndexReader.open(dir1, false);
459 assertRefCountEquals(1, reader1);
461 IndexReader initReader2 = IndexReader.open(dir2, false);
462 IndexReader multiReader1 = new MultiReader(new IndexReader[] {reader1, initReader2}, (mode == 0));
463 modifyIndex(0, dir2);
464 assertRefCountEquals(1 + mode, reader1);
466 IndexReader multiReader2 = multiReader1.reopen();
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 = reader1.reopen();
472 assertRefCountEquals(2 + mode, reader1);
478 modifyIndex(1, dir1);
479 IndexReader reader3 = reader2.reopen();
480 assertRefCountEquals(2 + mode, reader1);
481 assertRefCountEquals(1, reader2);
483 multiReader1.close();
484 assertRefCountEquals(1 + mode, reader1);
486 multiReader1.close();
487 assertRefCountEquals(1 + mode, reader1);
494 assertRefCountEquals(1, reader1);
496 multiReader2.close();
497 assertRefCountEquals(0, reader1);
499 multiReader2.close();
500 assertRefCountEquals(0, reader1);
503 assertRefCountEquals(0, reader1);
504 assertReaderClosed(reader1, true, false);
507 assertRefCountEquals(0, reader1);
508 assertReaderClosed(reader1, true, false);
511 assertRefCountEquals(0, reader1);
514 assertRefCountEquals(0, reader1);
515 assertReaderClosed(reader1, true, true);
522 public void testReferenceCountingParallelReader() throws IOException {
523 for (int mode = 0; mode <=1; mode++) {
524 Directory dir1 = newDirectory();
525 createIndex(random, dir1, false);
526 Directory dir2 = newDirectory();
527 createIndex(random, dir2, true);
529 IndexReader reader1 = IndexReader.open(dir1, false);
530 assertRefCountEquals(1, reader1);
532 ParallelReader parallelReader1 = new ParallelReader(mode == 0);
533 parallelReader1.add(reader1);
534 IndexReader initReader2 = IndexReader.open(dir2, false);
535 parallelReader1.add(initReader2);
536 modifyIndex(1, dir2);
537 assertRefCountEquals(1 + mode, reader1);
539 IndexReader parallelReader2 = parallelReader1.reopen();
540 // index1 hasn't changed, so parallelReader2 should share reader1 now with multiReader1
541 assertRefCountEquals(2 + mode, reader1);
543 modifyIndex(0, dir1);
544 modifyIndex(0, dir2);
545 IndexReader reader2 = reader1.reopen();
546 assertRefCountEquals(2 + mode, reader1);
552 modifyIndex(4, dir1);
553 IndexReader reader3 = reader2.reopen();
554 assertRefCountEquals(2 + mode, reader1);
555 assertRefCountEquals(1, reader2);
557 parallelReader1.close();
558 assertRefCountEquals(1 + mode, reader1);
560 parallelReader1.close();
561 assertRefCountEquals(1 + mode, reader1);
568 assertRefCountEquals(1, reader1);
570 parallelReader2.close();
571 assertRefCountEquals(0, reader1);
573 parallelReader2.close();
574 assertRefCountEquals(0, reader1);
577 assertRefCountEquals(0, reader1);
578 assertReaderClosed(reader1, true, false);
581 assertRefCountEquals(0, reader1);
582 assertReaderClosed(reader1, true, false);
585 assertRefCountEquals(0, reader1);
588 assertRefCountEquals(0, reader1);
589 assertReaderClosed(reader1, true, true);
597 public void testNormsRefCounting() throws IOException {
598 Directory dir1 = newDirectory();
599 createIndex(random, dir1, false);
601 IndexReader reader1 = IndexReader.open(dir1, false);
602 SegmentReader segmentReader1 = SegmentReader.getOnlySegmentReader(reader1);
603 IndexReader modifier = IndexReader.open(dir1, false);
604 modifier.deleteDocument(0);
607 IndexReader reader2 = reader1.reopen();
608 modifier = IndexReader.open(dir1, false);
609 modifier.setNorm(1, "field1", 50);
610 modifier.setNorm(1, "field2", 50);
613 IndexReader reader3 = reader2.reopen();
614 SegmentReader segmentReader3 = SegmentReader.getOnlySegmentReader(reader3);
615 modifier = IndexReader.open(dir1, false);
616 modifier.deleteDocument(2);
619 IndexReader reader4 = reader3.reopen();
620 modifier = IndexReader.open(dir1, false);
621 modifier.deleteDocument(3);
624 IndexReader reader5 = reader3.reopen();
626 // Now reader2-reader5 references reader1. reader1 and reader2
627 // share the same norms. reader3, reader4, reader5 also share norms.
628 assertRefCountEquals(1, reader1);
629 assertFalse(segmentReader1.normsClosed());
633 assertRefCountEquals(0, reader1);
634 assertFalse(segmentReader1.normsClosed());
637 assertRefCountEquals(0, reader1);
639 // now the norms for field1 and field2 should be closed
640 assertTrue(segmentReader1.normsClosed("field1"));
641 assertTrue(segmentReader1.normsClosed("field2"));
643 // but the norms for field3 and field4 should still be open
644 assertFalse(segmentReader1.normsClosed("field3"));
645 assertFalse(segmentReader1.normsClosed("field4"));
648 assertRefCountEquals(0, reader1);
649 assertFalse(segmentReader3.normsClosed());
651 assertRefCountEquals(0, reader1);
652 assertFalse(segmentReader3.normsClosed());
654 assertRefCountEquals(0, reader1);
656 // and now all norms that reader1 used should be closed
657 assertTrue(segmentReader1.normsClosed());
659 // now that reader3, reader4 and reader5 are closed,
660 // the norms that those three readers shared should be
662 assertTrue(segmentReader3.normsClosed());
667 private void performTestsWithExceptionInReopen(TestReopen test) throws Exception {
668 IndexReader index1 = test.openReader();
669 IndexReader index2 = test.openReader();
671 TestIndexReader.assertIndexEquals(index1, index2);
674 refreshReader(index1, test, 0, true);
675 fail("Expected exception not thrown.");
676 } catch (Exception e) {
677 // expected exception
680 // index2 should still be usable and unaffected by the failed reopen() call
681 TestIndexReader.assertIndexEquals(index1, index2);
687 public void testThreadSafety() throws Exception {
688 final Directory dir = newDirectory();
689 // NOTE: this also controls the number of threads!
690 final int n = _TestUtil.nextInt(random, 20, 40);
691 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
692 TEST_VERSION_CURRENT, new MockAnalyzer(random)));
693 for (int i = 0; i < n; i++) {
694 writer.addDocument(createDocument(i, 3));
699 final TestReopen test = new TestReopen() {
701 protected void modifyIndex(int i) throws IOException {
703 IndexReader modifier = IndexReader.open(dir, false);
704 modifier.setNorm(i, "field1", 50);
706 } else if (i % 3 == 1) {
707 IndexReader modifier = IndexReader.open(dir, false);
708 modifier.deleteDocument(i % modifier.maxDoc());
711 IndexWriter modifier = new IndexWriter(dir, new IndexWriterConfig(
712 TEST_VERSION_CURRENT, new MockAnalyzer(random)));
713 modifier.addDocument(createDocument(n + i, 6));
719 protected IndexReader openReader() throws IOException {
720 return IndexReader.open(dir, false);
724 final List<ReaderCouple> readers = Collections.synchronizedList(new ArrayList<ReaderCouple>());
725 IndexReader firstReader = IndexReader.open(dir, false);
726 IndexReader reader = firstReader;
727 final Random rnd = random;
729 ReaderThread[] threads = new ReaderThread[n];
730 final Set<IndexReader> readersToClose = Collections.synchronizedSet(new HashSet<IndexReader>());
732 for (int i = 0; i < n; i++) {
734 IndexReader refreshed = reader.reopen();
735 if (refreshed != reader) {
736 readersToClose.add(reader);
740 final IndexReader r = reader;
744 ReaderThreadTask task;
746 if (i < 4 || (i >=10 && i < 14) || i > 18) {
747 task = new ReaderThreadTask() {
750 public void run() throws Exception {
752 if (index % 2 == 0) {
753 // refresh reader synchronized
754 ReaderCouple c = (refreshReader(r, test, index, true));
755 readersToClose.add(c.newReader);
756 readersToClose.add(c.refreshedReader);
758 // prevent too many readers
762 IndexReader refreshed = r.reopen();
764 IndexSearcher searcher = newSearcher(refreshed);
765 ScoreDoc[] hits = searcher.search(
766 new TermQuery(new Term("field1", "a" + rnd.nextInt(refreshed.maxDoc()))),
767 null, 1000).scoreDocs;
768 if (hits.length > 0) {
769 searcher.doc(hits[0].doc);
772 if (refreshed != r) {
777 wait(_TestUtil.nextInt(random, 1, 100));
784 task = new ReaderThreadTask() {
786 public void run() throws Exception {
788 int numReaders = readers.size();
789 if (numReaders > 0) {
790 ReaderCouple c = readers.get(rnd.nextInt(numReaders));
791 TestIndexReader.assertIndexEquals(c.newReader, c.refreshedReader);
795 wait(_TestUtil.nextInt(random, 1, 100));
802 threads[i] = new ReaderThread(task);
810 for (int i = 0; i < n; i++) {
811 if (threads[i] != null) {
812 threads[i].stopThread();
816 for (int i = 0; i < n; i++) {
817 if (threads[i] != null) {
819 if (threads[i].error != null) {
820 String msg = "Error occurred in thread " + threads[i].getName() + ":\n" + threads[i].error.getMessage();
827 for (final IndexReader readerToClose : readersToClose) {
828 readerToClose.close();
834 for (final IndexReader readerToClose : readersToClose) {
835 assertReaderClosed(readerToClose, true, true);
838 assertReaderClosed(reader, true, true);
839 assertReaderClosed(firstReader, true, true);
844 private static class ReaderCouple {
845 ReaderCouple(IndexReader r1, IndexReader r2) {
847 refreshedReader = r2;
850 IndexReader newReader;
851 IndexReader refreshedReader;
854 private abstract static class ReaderThreadTask {
855 protected volatile boolean stopped;
860 public abstract void run() throws Exception;
863 private static class ReaderThread extends Thread {
864 private ReaderThreadTask task;
865 private Throwable error;
868 ReaderThread(ReaderThreadTask task) {
872 public void stopThread() {
880 } catch (Throwable r) {
881 r.printStackTrace(System.out);
887 private Object createReaderMutex = new Object();
889 private ReaderCouple refreshReader(IndexReader reader, boolean hasChanges) throws IOException {
890 return refreshReader(reader, null, -1, hasChanges);
893 ReaderCouple refreshReader(IndexReader reader, TestReopen test, int modify, boolean hasChanges) throws IOException {
894 synchronized (createReaderMutex) {
895 IndexReader r = null;
897 test.modifyIndex(modify);
898 r = test.openReader();
901 IndexReader refreshed = null;
903 refreshed = reader.reopen();
905 if (refreshed == null && r != null) {
906 // Hit exception -- close opened reader
912 if (refreshed == reader) {
913 fail("No new IndexReader instance created during refresh.");
916 if (refreshed != reader) {
917 fail("New IndexReader instance created during refresh even though index had no changes.");
921 return new ReaderCouple(r, refreshed);
925 public static void createIndex(Random random, Directory dir, boolean multiSegment) throws IOException {
926 IndexWriter.unlock(dir);
927 IndexWriter w = new IndexWriter(dir, LuceneTestCase.newIndexWriterConfig(random,
928 TEST_VERSION_CURRENT, new MockAnalyzer(random))
929 .setMergePolicy(new LogDocMergePolicy()));
931 for (int i = 0; i < 100; i++) {
932 w.addDocument(createDocument(i, 4));
933 if (multiSegment && (i % 10) == 0) {
944 IndexReader r = IndexReader.open(dir, false);
946 assertTrue(r.getSequentialSubReaders().length > 1);
948 assertTrue(r.getSequentialSubReaders().length == 1);
953 public static Document createDocument(int n, int numFields) {
954 StringBuilder sb = new StringBuilder();
955 Document doc = new Document();
958 doc.add(new Field("field1", sb.toString(), Store.YES, Index.ANALYZED));
959 doc.add(new Field("fielda", sb.toString(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));
960 doc.add(new Field("fieldb", sb.toString(), Store.YES, Index.NO));
963 for (int i = 1; i < numFields; i++) {
964 doc.add(new Field("field" + (i+1), sb.toString(), Store.YES, Index.ANALYZED));
969 static void modifyIndex(int i, Directory dir) throws IOException {
973 System.out.println("TEST: modify index");
975 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
976 w.setInfoStream(VERBOSE ? System.out : null);
977 w.deleteDocuments(new Term("field2", "a11"));
978 w.deleteDocuments(new Term("field2", "b30"));
983 IndexReader reader = IndexReader.open(dir, false);
984 reader.setNorm(4, "field1", 123);
985 reader.setNorm(44, "field2", 222);
986 reader.setNorm(44, "field4", 22);
991 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
997 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
998 w.addDocument(createDocument(101, 4));
1000 w.addDocument(createDocument(102, 4));
1001 w.addDocument(createDocument(103, 4));
1006 IndexReader reader = IndexReader.open(dir, false);
1007 reader.setNorm(5, "field1", 123);
1008 reader.setNorm(55, "field2", 222);
1013 IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
1014 w.addDocument(createDocument(101, 4));
1021 private void assertReaderClosed(IndexReader reader, boolean checkSubReaders, boolean checkNormsClosed) {
1022 assertEquals(0, reader.getRefCount());
1024 if (checkNormsClosed && reader instanceof SegmentReader) {
1025 assertTrue(((SegmentReader) reader).normsClosed());
1028 if (checkSubReaders) {
1029 if (reader instanceof DirectoryReader) {
1030 IndexReader[] subReaders = reader.getSequentialSubReaders();
1031 for (int i = 0; i < subReaders.length; i++) {
1032 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1036 if (reader instanceof MultiReader) {
1037 IndexReader[] subReaders = reader.getSequentialSubReaders();
1038 for (int i = 0; i < subReaders.length; i++) {
1039 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1043 if (reader instanceof ParallelReader) {
1044 IndexReader[] subReaders = ((ParallelReader) reader).getSubReaders();
1045 for (int i = 0; i < subReaders.length; i++) {
1046 assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1053 private void assertReaderOpen(IndexReader reader) {
1054 reader.ensureOpen();
1056 if (reader instanceof DirectoryReader) {
1057 IndexReader[] subReaders = reader.getSequentialSubReaders();
1058 for (int i = 0; i < subReaders.length; i++) {
1059 assertReaderOpen(subReaders[i]);
1065 private void assertRefCountEquals(int refCount, IndexReader reader) {
1066 assertEquals("Reader has wrong refCount value.", refCount, reader.getRefCount());
1070 private abstract static class TestReopen {
1071 protected abstract IndexReader openReader() throws IOException;
1072 protected abstract void modifyIndex(int i) throws IOException;
1075 public void testCloseOrig() throws Throwable {
1076 Directory dir = newDirectory();
1077 createIndex(random, dir, false);
1078 IndexReader r1 = IndexReader.open(dir, false);
1079 IndexReader r2 = IndexReader.open(dir, false);
1080 r2.deleteDocument(0);
1083 IndexReader r3 = r1.reopen();
1084 assertTrue(r1 != r3);
1088 fail("did not hit exception");
1089 } catch (AlreadyClosedException ace) {
1096 public void testDeletes() throws Throwable {
1097 Directory dir = newDirectory();
1098 createIndex(random, dir, false); // Create an index with a bunch of docs (1 segment)
1100 modifyIndex(0, dir); // Get delete bitVector on 1st segment
1101 modifyIndex(5, dir); // Add a doc (2 segments)
1103 IndexReader r1 = IndexReader.open(dir, false); // MSR
1105 modifyIndex(5, dir); // Add another doc (3 segments)
1107 IndexReader r2 = r1.reopen(); // MSR
1108 assertTrue(r1 != r2);
1110 SegmentReader sr1 = (SegmentReader) r1.getSequentialSubReaders()[0]; // Get SRs for the first segment from original
1111 SegmentReader sr2 = (SegmentReader) r2.getSequentialSubReaders()[0]; // and reopened IRs
1113 // At this point they share the same BitVector
1114 assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1116 r2.deleteDocument(0);
1118 // r1 should not see the delete
1119 assertFalse(r1.isDeleted(0));
1121 // Now r2 should have made a private copy of deleted docs:
1122 assertTrue(sr1.deletedDocs!=sr2.deletedDocs);
1129 public void testDeletes2() throws Throwable {
1130 Directory dir = newDirectory();
1131 createIndex(random, dir, false);
1132 // Get delete bitVector
1133 modifyIndex(0, dir);
1134 IndexReader r1 = IndexReader.open(dir, false);
1137 modifyIndex(5, dir);
1139 IndexReader r2 = r1.reopen();
1140 assertTrue(r1 != r2);
1142 IndexReader[] rs2 = r2.getSequentialSubReaders();
1144 SegmentReader sr1 = SegmentReader.getOnlySegmentReader(r1);
1145 SegmentReader sr2 = (SegmentReader) rs2[0];
1147 // At this point they share the same BitVector
1148 assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1149 final BitVector delDocs = sr1.deletedDocs;
1152 r2.deleteDocument(0);
1153 assertTrue(delDocs==sr2.deletedDocs);
1158 private static class KeepAllCommits implements IndexDeletionPolicy {
1159 public void onInit(List<? extends IndexCommit> commits) {
1161 public void onCommit(List<? extends IndexCommit> commits) {
1165 public void testReopenOnCommit() throws Throwable {
1166 Directory dir = newDirectory();
1167 IndexWriter writer = new IndexWriter(
1169 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1170 setIndexDeletionPolicy(new KeepAllCommits()).
1171 setMaxBufferedDocs(-1).
1172 setMergePolicy(newLogMergePolicy(10))
1174 for(int i=0;i<4;i++) {
1175 Document doc = new Document();
1176 doc.add(newField("id", ""+i, Field.Store.NO, Field.Index.NOT_ANALYZED));
1177 writer.addDocument(doc);
1178 Map<String,String> data = new HashMap<String,String>();
1179 data.put("index", i+"");
1180 writer.commit(data);
1182 for(int i=0;i<4;i++) {
1183 writer.deleteDocuments(new Term("id", ""+i));
1184 Map<String,String> data = new HashMap<String,String>();
1185 data.put("index", (4+i)+"");
1186 writer.commit(data);
1190 IndexReader r = IndexReader.open(dir, false);
1191 assertEquals(0, r.numDocs());
1193 Collection<IndexCommit> commits = IndexReader.listCommits(dir);
1194 for (final IndexCommit commit : commits) {
1195 IndexReader r2 = r.reopen(commit);
1196 assertTrue(r2 != r);
1198 // Reader should be readOnly
1200 r2.deleteDocument(0);
1201 fail("no exception hit");
1202 } catch (UnsupportedOperationException uoe) {
1206 final Map<String,String> s = commit.getUserData();
1208 if (s.size() == 0) {
1209 // First commit created by IW
1212 v = Integer.parseInt(s.get("index"));
1215 assertEquals(1+v, r2.numDocs());
1217 assertEquals(7-v, r2.numDocs());
1226 // LUCENE-1579: Make sure all SegmentReaders are new when
1227 // reopen switches readOnly
1228 public void testReopenChangeReadonly() throws Exception {
1229 Directory dir = newDirectory();
1230 IndexWriter writer = new IndexWriter(
1232 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1233 setMaxBufferedDocs(-1).
1234 setMergePolicy(newLogMergePolicy(10))
1236 Document doc = new Document();
1237 doc.add(newField("number", "17", Field.Store.NO, Field.Index.NOT_ANALYZED));
1238 writer.addDocument(doc);
1242 IndexReader r = IndexReader.open(dir, false);
1243 assertTrue(r instanceof DirectoryReader);
1244 IndexReader r1 = SegmentReader.getOnlySegmentReader(r);
1245 final int[] ints = FieldCache.DEFAULT.getInts(r1, "number");
1246 assertEquals(1, ints.length);
1247 assertEquals(17, ints[0]);
1249 // Reopen to readonly w/ no chnages
1250 IndexReader r3 = r.reopen(true);
1251 assertTrue(r3 instanceof ReadOnlyDirectoryReader);
1255 writer.addDocument(doc);
1258 // Reopen reader1 --> reader2
1259 IndexReader r2 = r.reopen(true);
1261 assertTrue(r2 instanceof ReadOnlyDirectoryReader);
1262 IndexReader[] subs = r2.getSequentialSubReaders();
1263 final int[] ints2 = FieldCache.DEFAULT.getInts(subs[0], "number");
1266 assertTrue(subs[0] instanceof ReadOnlySegmentReader);
1267 assertTrue(subs[1] instanceof ReadOnlySegmentReader);
1268 assertTrue(ints == ints2);