add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / test / org / apache / lucene / index / TestAddIndexes.java
1 package org.apache.lucene.index;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 import java.io.IOException;
21 import java.io.FileNotFoundException;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import org.apache.lucene.util.LuceneTestCase;
26 import org.apache.lucene.util._TestUtil;
27 import org.apache.lucene.analysis.MockAnalyzer;
28 import org.apache.lucene.document.Document;
29 import org.apache.lucene.document.Field;
30 import org.apache.lucene.document.Field.Index;
31 import org.apache.lucene.document.Field.Store;
32 import org.apache.lucene.document.Field.TermVector;
33 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
34 import org.apache.lucene.store.AlreadyClosedException;
35 import org.apache.lucene.store.Directory;
36 import org.apache.lucene.store.MockDirectoryWrapper;
37 import org.apache.lucene.store.RAMDirectory;
38
39 import org.apache.lucene.search.PhraseQuery;
40
41 public class TestAddIndexes extends LuceneTestCase {
42   
43   public void testSimpleCase() throws IOException {
44     // main directory
45     Directory dir = newDirectory();
46     // two auxiliary directories
47     Directory aux = newDirectory();
48     Directory aux2 = newDirectory();
49
50     IndexWriter writer = null;
51
52     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT,
53         new MockAnalyzer(random))
54         .setOpenMode(OpenMode.CREATE));
55     // add 100 documents
56     addDocs(writer, 100);
57     assertEquals(100, writer.maxDoc());
58     writer.close();
59
60     writer = newWriter(
61         aux,
62         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
63             setOpenMode(OpenMode.CREATE).
64             setMergePolicy(newLogMergePolicy(false))
65     );
66     // add 40 documents in separate files
67     addDocs(writer, 40);
68     assertEquals(40, writer.maxDoc());
69     writer.close();
70
71     writer = newWriter(aux2, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE));
72     // add 50 documents in compound files
73     addDocs2(writer, 50);
74     assertEquals(50, writer.maxDoc());
75     writer.close();
76
77     // test doc count before segments are merged
78     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
79     assertEquals(100, writer.maxDoc());
80     writer.addIndexes(new Directory[] { aux, aux2 });
81     assertEquals(190, writer.maxDoc());
82     writer.close();
83
84     // make sure the old index is correct
85     verifyNumDocs(aux, 40);
86
87     // make sure the new index is correct
88     verifyNumDocs(dir, 190);
89
90     // now add another set in.
91     Directory aux3 = newDirectory();
92     writer = newWriter(aux3, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
93     // add 40 documents
94     addDocs(writer, 40);
95     assertEquals(40, writer.maxDoc());
96     writer.close();
97
98     // test doc count before segments are merged/index is optimized
99     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
100     assertEquals(190, writer.maxDoc());
101     writer.addIndexes(new Directory[] { aux3 });
102     assertEquals(230, writer.maxDoc());
103     writer.close();
104
105     // make sure the new index is correct
106     verifyNumDocs(dir, 230);
107
108     verifyTermDocs(dir, new Term("content", "aaa"), 180);
109
110     verifyTermDocs(dir, new Term("content", "bbb"), 50);
111
112     // now optimize it.
113     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
114     writer.optimize();
115     writer.close();
116
117     // make sure the new index is correct
118     verifyNumDocs(dir, 230);
119
120     verifyTermDocs(dir, new Term("content", "aaa"), 180);
121
122     verifyTermDocs(dir, new Term("content", "bbb"), 50);
123
124     // now add a single document
125     Directory aux4 = newDirectory();
126     writer = newWriter(aux4, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
127     addDocs2(writer, 1);
128     writer.close();
129
130     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
131     assertEquals(230, writer.maxDoc());
132     writer.addIndexes(new Directory[] { aux4 });
133     assertEquals(231, writer.maxDoc());
134     writer.close();
135
136     verifyNumDocs(dir, 231);
137
138     verifyTermDocs(dir, new Term("content", "bbb"), 51);
139     dir.close();
140     aux.close();
141     aux2.close();
142     aux3.close();
143     aux4.close();
144   }
145
146   public void testWithPendingDeletes() throws IOException {
147     // main directory
148     Directory dir = newDirectory();
149     // auxiliary directory
150     Directory aux = newDirectory();
151
152     setUpDirs(dir, aux);
153     IndexWriter writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
154     writer.setInfoStream(VERBOSE ? System.out : null);
155     writer.setInfoStream(VERBOSE ? System.out : null);
156     writer.addIndexes(aux);
157
158     // Adds 10 docs, then replaces them with another 10
159     // docs, so 10 pending deletes:
160     for (int i = 0; i < 20; i++) {
161       Document doc = new Document();
162       doc.add(newField("id", "" + (i % 10), Field.Store.NO, Field.Index.NOT_ANALYZED));
163       doc.add(newField("content", "bbb " + i, Field.Store.NO,
164                         Field.Index.ANALYZED));
165       writer.updateDocument(new Term("id", "" + (i%10)), doc);
166     }
167     // Deletes one of the 10 added docs, leaving 9:
168     PhraseQuery q = new PhraseQuery();
169     q.add(new Term("content", "bbb"));
170     q.add(new Term("content", "14"));
171     writer.deleteDocuments(q);
172
173     writer.optimize();
174     writer.commit();
175
176     verifyNumDocs(dir, 1039);
177     verifyTermDocs(dir, new Term("content", "aaa"), 1030);
178     verifyTermDocs(dir, new Term("content", "bbb"), 9);
179
180     writer.close();
181     dir.close();
182     aux.close();
183   }
184
185   public void testWithPendingDeletes2() throws IOException {
186     // main directory
187     Directory dir = newDirectory();
188     // auxiliary directory
189     Directory aux = newDirectory();
190
191     setUpDirs(dir, aux);
192     IndexWriter writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
193     // Adds 10 docs, then replaces them with another 10
194     // docs, so 10 pending deletes:
195     for (int i = 0; i < 20; i++) {
196       Document doc = new Document();
197       doc.add(newField("id", "" + (i % 10), Field.Store.NO, Field.Index.NOT_ANALYZED));
198       doc.add(newField("content", "bbb " + i, Field.Store.NO, Field.Index.ANALYZED));
199       writer.updateDocument(new Term("id", "" + (i%10)), doc);
200     }
201     
202     writer.addIndexes(new Directory[] {aux});
203     
204     // Deletes one of the 10 added docs, leaving 9:
205     PhraseQuery q = new PhraseQuery();
206     q.add(new Term("content", "bbb"));
207     q.add(new Term("content", "14"));
208     writer.deleteDocuments(q);
209
210     writer.optimize();
211     writer.commit();
212
213     verifyNumDocs(dir, 1039);
214     verifyTermDocs(dir, new Term("content", "aaa"), 1030);
215     verifyTermDocs(dir, new Term("content", "bbb"), 9);
216
217     writer.close();
218     dir.close();
219     aux.close();
220   }
221
222   public void testWithPendingDeletes3() throws IOException {
223     // main directory
224     Directory dir = newDirectory();
225     // auxiliary directory
226     Directory aux = newDirectory();
227
228     setUpDirs(dir, aux);
229     IndexWriter writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
230
231     // Adds 10 docs, then replaces them with another 10
232     // docs, so 10 pending deletes:
233     for (int i = 0; i < 20; i++) {
234       Document doc = new Document();
235       doc.add(newField("id", "" + (i % 10), Field.Store.NO, Field.Index.NOT_ANALYZED));
236       doc.add(newField("content", "bbb " + i, Field.Store.NO,
237                         Field.Index.ANALYZED));
238       writer.updateDocument(new Term("id", "" + (i%10)), doc);
239     }
240
241     // Deletes one of the 10 added docs, leaving 9:
242     PhraseQuery q = new PhraseQuery();
243     q.add(new Term("content", "bbb"));
244     q.add(new Term("content", "14"));
245     writer.deleteDocuments(q);
246
247     writer.addIndexes(new Directory[] {aux});
248
249     writer.optimize();
250     writer.commit();
251
252     verifyNumDocs(dir, 1039);
253     verifyTermDocs(dir, new Term("content", "aaa"), 1030);
254     verifyTermDocs(dir, new Term("content", "bbb"), 9);
255
256     writer.close();
257     dir.close();
258     aux.close();
259   }
260
261   // case 0: add self or exceed maxMergeDocs, expect exception
262   public void testAddSelf() throws IOException {
263     // main directory
264     Directory dir = newDirectory();
265     // auxiliary directory
266     Directory aux = newDirectory();
267
268     IndexWriter writer = null;
269
270     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
271     // add 100 documents
272     addDocs(writer, 100);
273     assertEquals(100, writer.maxDoc());
274     writer.close();
275
276     writer = newWriter(
277         aux,
278         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
279             setOpenMode(OpenMode.CREATE).
280             setMaxBufferedDocs(1000).
281             setMergePolicy(newLogMergePolicy(false))
282     );
283     // add 140 documents in separate files
284     addDocs(writer, 40);
285     writer.close();
286     writer = newWriter(
287         aux,
288         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
289             setOpenMode(OpenMode.CREATE).
290             setMaxBufferedDocs(1000).
291             setMergePolicy(newLogMergePolicy(false))
292     );
293     addDocs(writer, 100);
294     writer.close();
295
296     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
297     try {
298       // cannot add self
299       writer.addIndexes(new Directory[] { aux, dir });
300       assertTrue(false);
301     }
302     catch (IllegalArgumentException e) {
303       assertEquals(100, writer.maxDoc());
304     }
305     writer.close();
306
307     // make sure the index is correct
308     verifyNumDocs(dir, 100);
309     dir.close();
310     aux.close();
311   }
312
313   // in all the remaining tests, make the doc count of the oldest segment
314   // in dir large so that it is never merged in addIndexes()
315   // case 1: no tail segments
316   public void testNoTailSegments() throws IOException {
317     // main directory
318     Directory dir = newDirectory();
319     // auxiliary directory
320     Directory aux = newDirectory();
321
322     setUpDirs(dir, aux);
323
324     IndexWriter writer = newWriter(
325         dir,
326         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
327             setOpenMode(OpenMode.APPEND).
328             setMaxBufferedDocs(10).
329             setMergePolicy(newLogMergePolicy(4))
330     );
331     addDocs(writer, 10);
332
333     writer.addIndexes(new Directory[] { aux });
334     assertEquals(1040, writer.maxDoc());
335     assertEquals(1000, writer.getDocCount(0));
336     writer.close();
337
338     // make sure the index is correct
339     verifyNumDocs(dir, 1040);
340     dir.close();
341     aux.close();
342   }
343
344   // case 2: tail segments, invariants hold, no copy
345   public void testNoCopySegments() throws IOException {
346     // main directory
347     Directory dir = newDirectory();
348     // auxiliary directory
349     Directory aux = newDirectory();
350
351     setUpDirs(dir, aux);
352
353     IndexWriter writer = newWriter(
354         dir,
355         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
356             setOpenMode(OpenMode.APPEND).
357             setMaxBufferedDocs(9).
358             setMergePolicy(newLogMergePolicy(4))
359     );
360     addDocs(writer, 2);
361
362     writer.addIndexes(new Directory[] { aux });
363     assertEquals(1032, writer.maxDoc());
364     assertEquals(1000, writer.getDocCount(0));
365     writer.close();
366
367     // make sure the index is correct
368     verifyNumDocs(dir, 1032);
369     dir.close();
370     aux.close();
371   }
372
373   // case 3: tail segments, invariants hold, copy, invariants hold
374   public void testNoMergeAfterCopy() throws IOException {
375     // main directory
376     Directory dir = newDirectory();
377     // auxiliary directory
378     Directory aux = newDirectory();
379
380     setUpDirs(dir, aux);
381
382     IndexWriter writer = newWriter(
383         dir,
384         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
385             setOpenMode(OpenMode.APPEND).
386             setMaxBufferedDocs(10).
387             setMergePolicy(newLogMergePolicy(4))
388     );
389
390     writer.addIndexes(new Directory[] { aux, new MockDirectoryWrapper(random, new RAMDirectory(aux)) });
391     assertEquals(1060, writer.maxDoc());
392     assertEquals(1000, writer.getDocCount(0));
393     writer.close();
394
395     // make sure the index is correct
396     verifyNumDocs(dir, 1060);
397     dir.close();
398     aux.close();
399   }
400
401   // case 4: tail segments, invariants hold, copy, invariants not hold
402   public void testMergeAfterCopy() throws IOException {
403     // main directory
404     Directory dir = newDirectory();
405     // auxiliary directory
406     Directory aux = newDirectory();
407
408     setUpDirs(dir, aux);
409
410     IndexReader reader = IndexReader.open(aux, false);
411     for (int i = 0; i < 20; i++) {
412       reader.deleteDocument(i);
413     }
414     assertEquals(10, reader.numDocs());
415     reader.close();
416
417     IndexWriter writer = newWriter(
418         dir,
419         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
420             setOpenMode(OpenMode.APPEND).
421             setMaxBufferedDocs(4).
422             setMergePolicy(newLogMergePolicy(4))
423     );
424
425     writer.addIndexes(new Directory[] { aux, new MockDirectoryWrapper(random, new RAMDirectory(aux)) });
426     assertEquals(1020, writer.maxDoc());
427     assertEquals(1000, writer.getDocCount(0));
428     writer.close();
429     dir.close();
430     aux.close();
431   }
432
433   // case 5: tail segments, invariants not hold
434   public void testMoreMerges() throws IOException {
435     // main directory
436     Directory dir = newDirectory();
437     // auxiliary directory
438     Directory aux = newDirectory();
439     Directory aux2 = newDirectory();
440
441     setUpDirs(dir, aux);
442
443     IndexWriter writer = newWriter(
444         aux2,
445         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
446             setOpenMode(OpenMode.CREATE).
447             setMaxBufferedDocs(100).
448             setMergePolicy(newLogMergePolicy(10))
449     );
450     writer.setInfoStream(VERBOSE ? System.out : null);
451     writer.addIndexes(aux);
452     assertEquals(30, writer.maxDoc());
453     writer.close();
454
455     IndexReader reader = IndexReader.open(aux, false);
456     for (int i = 0; i < 27; i++) {
457       reader.deleteDocument(i);
458     }
459     assertEquals(3, reader.numDocs());
460     reader.close();
461
462     reader = IndexReader.open(aux2, false);
463     for (int i = 0; i < 8; i++) {
464       reader.deleteDocument(i);
465     }
466     assertEquals(22, reader.numDocs());
467     reader.close();
468
469     writer = newWriter(
470         dir,
471         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
472             setOpenMode(OpenMode.APPEND).
473             setMaxBufferedDocs(6).
474             setMergePolicy(newLogMergePolicy(4))
475     );
476
477     writer.addIndexes(new Directory[] { aux, aux2 });
478     assertEquals(1040, writer.maxDoc());
479     assertEquals(1000, writer.getDocCount(0));
480     writer.close();
481     dir.close();
482     aux.close();
483     aux2.close();
484   }
485
486   private IndexWriter newWriter(Directory dir, IndexWriterConfig conf)
487       throws IOException {
488     conf.setMergePolicy(new LogDocMergePolicy());
489     final IndexWriter writer = new IndexWriter(dir, conf);
490     return writer;
491   }
492
493   private void addDocs(IndexWriter writer, int numDocs) throws IOException {
494     for (int i = 0; i < numDocs; i++) {
495       Document doc = new Document();
496       doc.add(newField("content", "aaa", Field.Store.NO,
497                         Field.Index.ANALYZED));
498       writer.addDocument(doc);
499     }
500   }
501
502   private void addDocs2(IndexWriter writer, int numDocs) throws IOException {
503     for (int i = 0; i < numDocs; i++) {
504       Document doc = new Document();
505       doc.add(newField("content", "bbb", Field.Store.NO,
506                         Field.Index.ANALYZED));
507       writer.addDocument(doc);
508     }
509   }
510
511   private void verifyNumDocs(Directory dir, int numDocs) throws IOException {
512     IndexReader reader = IndexReader.open(dir, true);
513     assertEquals(numDocs, reader.maxDoc());
514     assertEquals(numDocs, reader.numDocs());
515     reader.close();
516   }
517
518   private void verifyTermDocs(Directory dir, Term term, int numDocs)
519       throws IOException {
520     IndexReader reader = IndexReader.open(dir, true);
521     TermDocs termDocs = reader.termDocs(term);
522     int count = 0;
523     while (termDocs.next())
524       count++;
525     assertEquals(numDocs, count);
526     reader.close();
527   }
528
529   private void setUpDirs(Directory dir, Directory aux) throws IOException {
530     IndexWriter writer = null;
531
532     writer = newWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE).setMaxBufferedDocs(1000));
533     // add 1000 documents in 1 segment
534     addDocs(writer, 1000);
535     assertEquals(1000, writer.maxDoc());
536     assertEquals(1, writer.getSegmentCount());
537     writer.close();
538
539     writer = newWriter(
540         aux,
541         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
542             setOpenMode(OpenMode.CREATE).
543             setMaxBufferedDocs(1000).
544             setMergePolicy(newLogMergePolicy(false, 10))
545     );
546     // add 30 documents in 3 segments
547     for (int i = 0; i < 3; i++) {
548       addDocs(writer, 10);
549       writer.close();
550       writer = newWriter(
551           aux,
552           newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
553               setOpenMode(OpenMode.APPEND).
554               setMaxBufferedDocs(1000).
555               setMergePolicy(newLogMergePolicy(false, 10))
556       );
557     }
558     assertEquals(30, writer.maxDoc());
559     assertEquals(3, writer.getSegmentCount());
560     writer.close();
561   }
562
563   // LUCENE-1270
564   public void testHangOnClose() throws IOException {
565
566     Directory dir = newDirectory();
567     LogByteSizeMergePolicy lmp = new LogByteSizeMergePolicy();
568     lmp.setUseCompoundFile(false);
569     lmp.setMergeFactor(100);
570     IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
571         TEST_VERSION_CURRENT, new MockAnalyzer(random))
572         .setMaxBufferedDocs(5).setMergePolicy(lmp));
573
574     Document doc = new Document();
575     doc.add(newField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", Field.Store.YES,
576                       Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
577     for(int i=0;i<60;i++)
578       writer.addDocument(doc);
579
580     Document doc2 = new Document();
581     doc2.add(newField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", Field.Store.YES,
582                       Field.Index.NO));
583     doc2.add(newField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", Field.Store.YES,
584                       Field.Index.NO));
585     doc2.add(newField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", Field.Store.YES,
586                       Field.Index.NO));
587     doc2.add(newField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", Field.Store.YES,
588                       Field.Index.NO));
589     for(int i=0;i<10;i++)
590       writer.addDocument(doc2);
591     writer.close();
592
593     Directory dir2 = newDirectory();
594     lmp = new LogByteSizeMergePolicy();
595     lmp.setMinMergeMB(0.0001);
596     lmp.setUseCompoundFile(false);
597     lmp.setMergeFactor(4);
598     writer = new IndexWriter(dir2, newIndexWriterConfig(TEST_VERSION_CURRENT,
599         new MockAnalyzer(random))
600         .setMergeScheduler(new SerialMergeScheduler()).setMergePolicy(lmp));
601     writer.addIndexes(new Directory[] {dir});
602     writer.close();
603     dir.close();
604     dir2.close();
605   }
606
607   // TODO: these are also in TestIndexWriter... add a simple doc-writing method
608   // like this to LuceneTestCase?
609   private void addDoc(IndexWriter writer) throws IOException
610   {
611       Document doc = new Document();
612       doc.add(newField("content", "aaa", Field.Store.NO, Field.Index.ANALYZED));
613       writer.addDocument(doc);
614   }
615   
616   private abstract class RunAddIndexesThreads {
617
618     Directory dir, dir2;
619     final static int NUM_INIT_DOCS = 17;
620     IndexWriter writer2;
621     final List<Throwable> failures = new ArrayList<Throwable>();
622     volatile boolean didClose;
623     final IndexReader[] readers;
624     final int NUM_COPY;
625     final static int NUM_THREADS = 5;
626     final Thread[] threads = new Thread[NUM_THREADS];
627
628     public RunAddIndexesThreads(int numCopy) throws Throwable {
629       NUM_COPY = numCopy;
630       dir = new MockDirectoryWrapper(random, new RAMDirectory());
631       IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(
632           TEST_VERSION_CURRENT, new MockAnalyzer(random))
633           .setMaxBufferedDocs(2));
634       for (int i = 0; i < NUM_INIT_DOCS; i++)
635         addDoc(writer);
636       writer.close();
637
638       dir2 = newDirectory();
639       writer2 = new IndexWriter(dir2, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
640       writer2.setInfoStream(VERBOSE ? System.out : null);
641       writer2.commit();
642       
643
644       readers = new IndexReader[NUM_COPY];
645       for(int i=0;i<NUM_COPY;i++)
646         readers[i] = IndexReader.open(dir, true);
647     }
648
649     void launchThreads(final int numIter) {
650
651       for(int i=0;i<NUM_THREADS;i++) {
652         threads[i] = new Thread() {
653             @Override
654             public void run() {
655               try {
656
657                 final Directory[] dirs = new Directory[NUM_COPY];
658                 for(int k=0;k<NUM_COPY;k++)
659                   dirs[k] = new MockDirectoryWrapper(random, new RAMDirectory(dir));
660
661                 int j=0;
662
663                 while(true) {
664                   // System.out.println(Thread.currentThread().getName() + ": iter j=" + j);
665                   if (numIter > 0 && j == numIter)
666                     break;
667                   doBody(j++, dirs);
668                 }
669               } catch (Throwable t) {
670                 handle(t);
671               }
672             }
673           };
674       }
675
676       for(int i=0;i<NUM_THREADS;i++)
677         threads[i].start();
678     }
679
680     void joinThreads() throws Exception {
681       for(int i=0;i<NUM_THREADS;i++)
682         threads[i].join();
683     }
684
685     void close(boolean doWait) throws Throwable {
686       didClose = true;
687       writer2.close(doWait);
688     }
689
690     void closeDir() throws Throwable {
691       for(int i=0;i<NUM_COPY;i++)
692         readers[i].close();
693       dir2.close();
694     }
695
696     abstract void doBody(int j, Directory[] dirs) throws Throwable;
697     abstract void handle(Throwable t);
698   }
699
700   private class CommitAndAddIndexes extends RunAddIndexesThreads {
701     public CommitAndAddIndexes(int numCopy) throws Throwable {
702       super(numCopy);
703     }
704
705     @Override
706     void handle(Throwable t) {
707       t.printStackTrace(System.out);
708       synchronized(failures) {
709         failures.add(t);
710       }
711     }
712
713     @Override
714     void doBody(int j, Directory[] dirs) throws Throwable {
715       switch(j%5) {
716       case 0:
717         if (VERBOSE) {
718           System.out.println(Thread.currentThread().getName() + ": TEST: addIndexes(Dir[]) then optimize");
719         }
720         writer2.addIndexes(dirs);
721         writer2.optimize();
722         break;
723       case 1:
724         if (VERBOSE) {
725           System.out.println(Thread.currentThread().getName() + ": TEST: addIndexes(Dir[])");
726         }
727         writer2.addIndexes(dirs);
728         break;
729       case 2:
730         if (VERBOSE) {
731           System.out.println(Thread.currentThread().getName() + ": TEST: addIndexes(IndexReader[])");
732         }
733         writer2.addIndexes(readers);
734         break;
735       case 3:
736         if (VERBOSE) {
737           System.out.println(Thread.currentThread().getName() + ": TEST: addIndexes(Dir[]) then maybeMerge");
738         }
739         writer2.addIndexes(dirs);
740         writer2.maybeMerge();
741         break;
742       case 4:
743         if (VERBOSE) {
744           System.out.println(Thread.currentThread().getName() + ": TEST: commit");
745         }
746         writer2.commit();
747       }
748     }
749   }
750   
751   // LUCENE-1335: test simultaneous addIndexes & commits
752   // from multiple threads
753   public void testAddIndexesWithThreads() throws Throwable {
754
755     final int NUM_ITER = TEST_NIGHTLY ? 15 : 5;
756     final int NUM_COPY = 3;
757     CommitAndAddIndexes c = new CommitAndAddIndexes(NUM_COPY);
758     c.writer2.setInfoStream(VERBOSE ? System.out : null);
759     c.launchThreads(NUM_ITER);
760
761     for(int i=0;i<100;i++)
762       addDoc(c.writer2);
763
764     c.joinThreads();
765
766     int expectedNumDocs = 100+NUM_COPY*(4*NUM_ITER/5)*RunAddIndexesThreads.NUM_THREADS*RunAddIndexesThreads.NUM_INIT_DOCS;
767     assertEquals(expectedNumDocs, c.writer2.numDocs());
768
769     c.close(true);
770
771     assertTrue(c.failures.size() == 0);
772
773     IndexReader reader = IndexReader.open(c.dir2, true);
774     assertEquals(expectedNumDocs, reader.numDocs());
775     reader.close();
776
777     c.closeDir();
778   }
779
780   private class CommitAndAddIndexes2 extends CommitAndAddIndexes {
781     public CommitAndAddIndexes2(int numCopy) throws Throwable {
782       super(numCopy);
783     }
784
785     @Override
786     void handle(Throwable t) {
787       if (!(t instanceof AlreadyClosedException) && !(t instanceof NullPointerException)) {
788         t.printStackTrace(System.out);
789         synchronized(failures) {
790           failures.add(t);
791         }
792       }
793     }
794   }
795
796   // LUCENE-1335: test simultaneous addIndexes & close
797   public void testAddIndexesWithClose() throws Throwable {
798     final int NUM_COPY = 3;
799     CommitAndAddIndexes2 c = new CommitAndAddIndexes2(NUM_COPY);
800     //c.writer2.setInfoStream(System.out);
801     c.launchThreads(-1);
802
803     // Close w/o first stopping/joining the threads
804     c.close(true);
805     //c.writer2.close();
806
807     c.joinThreads();
808
809     c.closeDir();
810
811     assertTrue(c.failures.size() == 0);
812   }
813
814   private class CommitAndAddIndexes3 extends RunAddIndexesThreads {
815     public CommitAndAddIndexes3(int numCopy) throws Throwable {
816       super(numCopy);
817     }
818
819     @Override
820     void doBody(int j, Directory[] dirs) throws Throwable {
821       switch(j%5) {
822       case 0:
823         if (VERBOSE) {
824           System.out.println("TEST: " + Thread.currentThread().getName() + ": addIndexes + optimize");
825         }
826         writer2.addIndexes(dirs);
827         writer2.optimize();
828         break;
829       case 1:
830         if (VERBOSE) {
831           System.out.println("TEST: " + Thread.currentThread().getName() + ": addIndexes");
832         }
833         writer2.addIndexes(dirs);
834         break;
835       case 2:
836         if (VERBOSE) {
837           System.out.println("TEST: " + Thread.currentThread().getName() + ": addIndexes(IR[])");
838         }
839         writer2.addIndexes(readers);
840         break;
841       case 3:
842         if (VERBOSE) {
843           System.out.println("TEST: " + Thread.currentThread().getName() + ": optimize");
844         }
845         writer2.optimize();
846         break;
847       case 4:
848         if (VERBOSE) {
849           System.out.println("TEST: " + Thread.currentThread().getName() + ": commit");
850         }
851         writer2.commit();
852       }
853     }
854
855     @Override
856     void handle(Throwable t) {
857       boolean report = true;
858
859       if (t instanceof AlreadyClosedException || t instanceof MergePolicy.MergeAbortedException || t instanceof NullPointerException) {
860         report = !didClose;
861       } else if (t instanceof FileNotFoundException)  {
862         report = !didClose;
863       } else if (t instanceof IOException)  {
864         Throwable t2 = t.getCause();
865         if (t2 instanceof MergePolicy.MergeAbortedException) {
866           report = !didClose;
867         }
868       }
869       if (report) {
870         t.printStackTrace(System.out);
871         synchronized(failures) {
872           failures.add(t);
873         }
874       }
875     }
876   }
877
878   // LUCENE-1335: test simultaneous addIndexes & close
879   public void testAddIndexesWithCloseNoWait() throws Throwable {
880
881     final int NUM_COPY = 50;
882     CommitAndAddIndexes3 c = new CommitAndAddIndexes3(NUM_COPY);
883     if (VERBOSE) {
884       c.writer2.setInfoStream(System.out);
885     }
886     c.launchThreads(-1);
887
888     Thread.sleep(_TestUtil.nextInt(random, 10, 500));
889
890     // Close w/o first stopping/joining the threads
891     if (VERBOSE) {
892       System.out.println("TEST: now close(false)");
893     }
894     c.close(false);
895
896     c.joinThreads();
897
898     if (VERBOSE) {
899       System.out.println("TEST: done join threads");
900     }
901     c.closeDir();
902
903     assertTrue(c.failures.size() == 0);
904   }
905
906   // LUCENE-1335: test simultaneous addIndexes & close
907   public void testAddIndexesWithRollback() throws Throwable {
908
909     final int NUM_COPY = TEST_NIGHTLY ? 50 : 5;
910     CommitAndAddIndexes3 c = new CommitAndAddIndexes3(NUM_COPY);
911     c.launchThreads(-1);
912
913     Thread.sleep(_TestUtil.nextInt(random, 10, 500));
914
915     // Close w/o first stopping/joining the threads
916     if (VERBOSE) {
917       System.out.println("TEST: now force rollback");
918     }
919     c.didClose = true;
920     c.writer2.rollback();
921
922     c.joinThreads();
923
924     c.closeDir();
925
926     assertTrue(c.failures.size() == 0);
927   }
928
929   // LUCENE-2790: tests that the non CFS files were deleted by addIndexes
930   public void testNonCFSLeftovers() throws Exception {
931     Directory[] dirs = new Directory[2];
932     for (int i = 0; i < dirs.length; i++) {
933       dirs[i] = new RAMDirectory();
934       IndexWriter w = new IndexWriter(dirs[i], new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
935       Document d = new Document();
936       d.add(new Field("c", "v", Store.YES, Index.ANALYZED, TermVector.YES));
937       w.addDocument(d);
938       w.close();
939     }
940     
941     IndexReader[] readers = new IndexReader[] { IndexReader.open(dirs[0]), IndexReader.open(dirs[1]) };
942     
943     Directory dir = new RAMDirectory();
944     IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy());
945     LogMergePolicy lmp = (LogMergePolicy) conf.getMergePolicy();
946     lmp.setNoCFSRatio(1.0); // Force creation of CFS
947     lmp.setUseCompoundFile(true);
948     IndexWriter w3 = new IndexWriter(dir, conf);
949     w3.addIndexes(readers);
950     w3.close();
951     
952     assertEquals("Only one compound segment should exist", 3, dir.listAll().length);
953   }
954  
955   // LUCENE-2996: tests that addIndexes(IndexReader) applies existing deletes correctly.
956   public void testExistingDeletes() throws Exception {
957     Directory[] dirs = new Directory[2];
958     for (int i = 0; i < dirs.length; i++) {
959       dirs[i] = newDirectory();
960       IndexWriterConfig conf = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
961       IndexWriter writer = new IndexWriter(dirs[i], conf);
962       Document doc = new Document();
963       doc.add(new Field("id", "myid", Store.NO, Index.NOT_ANALYZED_NO_NORMS));
964       writer.addDocument(doc);
965       writer.close();
966     }
967
968     IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
969     IndexWriter writer = new IndexWriter(dirs[0], conf);
970
971     // Now delete the document
972     writer.deleteDocuments(new Term("id", "myid"));
973     IndexReader r = IndexReader.open(dirs[1]);
974     try {
975       writer.addIndexes(r);
976     } finally {
977       r.close();
978     }
979     writer.commit();
980     assertEquals("Documents from the incoming index should not have been deleted", 1, writer.numDocs());
981     writer.close();
982
983     for (Directory dir : dirs) {
984       dir.close();
985     }
986
987   }
988   
989   // LUCENE-3126: tests that if a non-CFS segment is copied, it is converted to
990   // a CFS, given MP preferences
991   public void testCopyIntoCFS() throws Exception {
992     // create an index, no CFS (so we can assert that existing segments are not affected)
993     Directory target = newDirectory();
994     LogMergePolicy lmp = newLogMergePolicy(false);
995     IndexWriterConfig conf = newIndexWriterConfig(TEST_VERSION_CURRENT, null).setMergePolicy(lmp);
996     IndexWriter w = new IndexWriter(target, conf);
997     w.addDocument(new Document());
998     w.commit();
999     assertFalse(w.segmentInfos.info(0).getUseCompoundFile());
1000
1001     // prepare second index, no-CFS too + .del file + separate norms file
1002     Directory src = newDirectory();
1003     LogMergePolicy lmp2 = newLogMergePolicy(false);
1004     IndexWriterConfig conf2 = newIndexWriterConfig(TEST_VERSION_CURRENT,
1005         new MockAnalyzer(random)).setMergePolicy(lmp2);
1006     IndexWriter w2 = new IndexWriter(src, conf2);
1007     Document doc = new Document();
1008     doc.add(new Field("c", "some text", Store.YES, Index.ANALYZED));
1009     w2.addDocument(doc);
1010     doc = new Document();
1011     doc.add(new Field("d", "delete", Store.NO, Index.NOT_ANALYZED_NO_NORMS));
1012     w2.addDocument(doc);
1013     w2.commit();
1014     w2.deleteDocuments(new Term("d", "delete"));
1015     w2.commit();
1016     w2.close();
1017
1018     // create separate norms file
1019     IndexReader r = IndexReader.open(src, false);
1020     r.setNorm(0, "c", (byte) 1);
1021     r.close();
1022     assertTrue(".del file not found", src.fileExists("_0_1.del"));
1023     assertTrue("separate norms file not found", src.fileExists("_0_1.s0"));
1024     
1025     // Case 1: force 'CFS' on target
1026     lmp.setUseCompoundFile(true);
1027     lmp.setNoCFSRatio(1.0);
1028     w.addIndexes(src);
1029     w.commit();
1030     assertFalse("existing segments should not be modified by addIndexes", w.segmentInfos.info(0).getUseCompoundFile());
1031     assertTrue("segment should have been converted to a CFS by addIndexes", w.segmentInfos.info(1).getUseCompoundFile());
1032     assertTrue(".del file not found", target.fileExists("_1_1.del"));
1033     assertTrue("separate norms file not found", target.fileExists("_1_1.s0"));
1034
1035     // Case 2: LMP disallows CFS
1036     lmp.setUseCompoundFile(false);
1037     w.addIndexes(src);
1038     w.commit();
1039     assertFalse("segment should not have been converted to a CFS by addIndexes if MP disallows", w.segmentInfos.info(2).getUseCompoundFile());
1040
1041     w.close();
1042     
1043     // cleanup
1044     src.close();
1045     target.close();
1046   }
1047
1048 }