pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test / org / apache / lucene / index / TestIndexReaderClone.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 org.apache.lucene.search.Similarity;
21 import org.apache.lucene.analysis.MockAnalyzer;
22 import org.apache.lucene.document.Document;
23 import org.apache.lucene.document.Field;
24 import org.apache.lucene.store.Directory;
25 import org.apache.lucene.store.LockObtainFailedException;
26 import org.apache.lucene.util.LuceneTestCase;
27
28 /**
29  * Tests cloning multiple types of readers, modifying the deletedDocs and norms
30  * and verifies copy on write semantics of the deletedDocs and norms is
31  * implemented properly
32  */
33 public class TestIndexReaderClone extends LuceneTestCase {
34   
35   public void testCloneReadOnlySegmentReader() throws Exception {
36     final Directory dir1 = newDirectory();
37
38     TestIndexReaderReopen.createIndex(random, dir1, false);
39     IndexReader reader = IndexReader.open(dir1, false);
40     IndexReader readOnlyReader = reader.clone(true);
41     if (!isReadOnly(readOnlyReader)) {
42       fail("reader isn't read only");
43     }
44     if (deleteWorked(1, readOnlyReader)) {
45       fail("deleting from the original should not have worked");
46     }
47     reader.close();
48     readOnlyReader.close();
49     dir1.close();
50   }
51
52   // open non-readOnly reader1, clone to non-readOnly
53   // reader2, make sure we can change reader2
54   public void testCloneNoChangesStillReadOnly() throws Exception {
55     final Directory dir1 = newDirectory();
56
57     TestIndexReaderReopen.createIndex(random, dir1, true);
58     IndexReader r1 = IndexReader.open(dir1, false);
59     IndexReader r2 = r1.clone(false);
60     if (!deleteWorked(1, r2)) {
61       fail("deleting from the cloned should have worked");
62     }
63     r1.close();
64     r2.close();
65     dir1.close();
66   }
67   
68   // open non-readOnly reader1, clone to non-readOnly
69   // reader2, make sure we can change reader1
70   public void testCloneWriteToOrig() throws Exception {
71     final Directory dir1 = newDirectory();
72
73     TestIndexReaderReopen.createIndex(random, dir1, true);
74     IndexReader r1 = IndexReader.open(dir1, false);
75     IndexReader r2 = r1.clone(false);
76     if (!deleteWorked(1, r1)) {
77       fail("deleting from the original should have worked");
78     }
79     r1.close();
80     r2.close();
81     dir1.close();
82   }
83   
84   // open non-readOnly reader1, clone to non-readOnly
85   // reader2, make sure we can change reader2
86   public void testCloneWriteToClone() throws Exception {
87     final Directory dir1 = newDirectory();
88
89     TestIndexReaderReopen.createIndex(random, dir1, true);
90     IndexReader r1 = IndexReader.open(dir1, false);
91     IndexReader r2 = r1.clone(false);
92     if (!deleteWorked(1, r2)) {
93       fail("deleting from the original should have worked");
94     }
95     // should fail because reader1 holds the write lock
96     assertTrue("first reader should not be able to delete", !deleteWorked(1, r1));
97     r2.close();
98     // should fail because we are now stale (reader1
99     // committed changes)
100     assertTrue("first reader should not be able to delete", !deleteWorked(1, r1));
101     r1.close();
102
103     dir1.close();
104   }
105   
106   // create single-segment index, open non-readOnly
107   // SegmentReader, add docs, reopen to multireader, then do
108   // delete
109   public void testReopenSegmentReaderToMultiReader() throws Exception {
110     final Directory dir1 = newDirectory();
111
112     TestIndexReaderReopen.createIndex(random, dir1, false);
113     IndexReader reader1 = IndexReader.open(dir1, false);
114
115     TestIndexReaderReopen.modifyIndex(5, dir1);
116     
117     IndexReader reader2 = IndexReader.openIfChanged(reader1);
118     assertNotNull(reader2);
119     assertTrue(reader1 != reader2);
120
121     assertTrue(deleteWorked(1, reader2));
122     reader1.close();
123     reader2.close();
124     dir1.close();
125   }
126
127   // open non-readOnly reader1, clone to readOnly reader2
128   public void testCloneWriteableToReadOnly() throws Exception {
129     final Directory dir1 = newDirectory();
130
131     TestIndexReaderReopen.createIndex(random, dir1, true);
132     IndexReader reader = IndexReader.open(dir1, false);
133     IndexReader readOnlyReader = reader.clone(true);
134     if (!isReadOnly(readOnlyReader)) {
135       fail("reader isn't read only");
136     }
137     if (deleteWorked(1, readOnlyReader)) {
138       fail("deleting from the original should not have worked");
139     }
140     // this readonly reader shouldn't have a write lock
141     if (readOnlyReader.hasChanges) {
142       fail("readOnlyReader has a write lock");
143     }
144     reader.close();
145     readOnlyReader.close();
146     dir1.close();
147   }
148
149   // open non-readOnly reader1, reopen to readOnly reader2
150   public void testReopenWriteableToReadOnly() throws Exception {
151     final Directory dir1 = newDirectory();
152
153     TestIndexReaderReopen.createIndex(random, dir1, true);
154     IndexReader reader = IndexReader.open(dir1, false);
155     final int docCount = reader.numDocs();
156     assertTrue(deleteWorked(1, reader));
157     assertEquals(docCount-1, reader.numDocs());
158
159     IndexReader readOnlyReader = IndexReader.openIfChanged(reader, true);
160     assertNotNull(readOnlyReader);
161     if (!isReadOnly(readOnlyReader)) {
162       fail("reader isn't read only");
163     }
164     assertFalse(deleteWorked(1, readOnlyReader));
165     assertEquals(docCount-1, readOnlyReader.numDocs());
166     reader.close();
167     readOnlyReader.close();
168     dir1.close();
169   }
170
171   // open readOnly reader1, clone to non-readOnly reader2
172   public void testCloneReadOnlyToWriteable() throws Exception {
173     final Directory dir1 = newDirectory();
174
175     TestIndexReaderReopen.createIndex(random, dir1, true);
176     IndexReader reader1 = IndexReader.open(dir1, true);
177
178     IndexReader reader2 = reader1.clone(false);
179     if (isReadOnly(reader2)) {
180       fail("reader should not be read only");
181     }
182     assertFalse("deleting from the original reader should not have worked", deleteWorked(1, reader1));
183     // this readonly reader shouldn't yet have a write lock
184     if (reader2.hasChanges) {
185       fail("cloned reader should not have write lock");
186     }
187     assertTrue("deleting from the cloned reader should have worked", deleteWorked(1, reader2));
188     reader1.close();
189     reader2.close();
190     dir1.close();
191   }
192
193   // open non-readOnly reader1 on multi-segment index, then
194   // fully merge the index, then clone to readOnly reader2
195   public void testReadOnlyCloneAfterFullMerge() throws Exception {
196     final Directory dir1 = newDirectory();
197
198     TestIndexReaderReopen.createIndex(random, dir1, true);
199     IndexReader reader1 = IndexReader.open(dir1, false);
200     IndexWriter w = new IndexWriter(dir1, newIndexWriterConfig(
201         TEST_VERSION_CURRENT, new MockAnalyzer(random)));
202     w.forceMerge(1);
203     w.close();
204     IndexReader reader2 = reader1.clone(true);
205     assertTrue(isReadOnly(reader2));
206     reader1.close();
207     reader2.close();
208     dir1.close();
209   }
210   
211   private static boolean deleteWorked(int doc, IndexReader r) {
212     boolean exception = false;
213     try {
214       // trying to delete from the original reader should throw an exception
215       r.deleteDocument(doc);
216     } catch (Exception ex) {
217       exception = true;
218     }
219     return !exception;
220   }
221   
222   public void testCloneReadOnlyDirectoryReader() throws Exception {
223     final Directory dir1 = newDirectory();
224
225     TestIndexReaderReopen.createIndex(random, dir1, true);
226     IndexReader reader = IndexReader.open(dir1, false);
227     IndexReader readOnlyReader = reader.clone(true);
228     if (!isReadOnly(readOnlyReader)) {
229       fail("reader isn't read only");
230     }
231     reader.close();
232     readOnlyReader.close();
233     dir1.close();
234   }
235
236   public static boolean isReadOnly(IndexReader r) {
237     if (r instanceof ReadOnlySegmentReader
238         || r instanceof ReadOnlyDirectoryReader)
239       return true;
240     return false;
241   }
242
243   public void testParallelReader() throws Exception {
244     final Directory dir1 = newDirectory();
245     TestIndexReaderReopen.createIndex(random, dir1, true);
246     final Directory dir2 = newDirectory();
247     TestIndexReaderReopen.createIndex(random, dir2, true);
248     IndexReader r1 = IndexReader.open(dir1, false);
249     IndexReader r2 = IndexReader.open(dir2, false);
250
251     ParallelReader pr1 = new ParallelReader();
252     pr1.add(r1);
253     pr1.add(r2);
254
255     performDefaultTests(pr1);
256     pr1.close();
257     dir1.close();
258     dir2.close();
259   }
260
261   /**
262    * 1. Get a norm from the original reader 2. Clone the original reader 3.
263    * Delete a document and set the norm of the cloned reader 4. Verify the norms
264    * are not the same on each reader 5. Verify the doc deleted is only in the
265    * cloned reader 6. Try to delete a document in the original reader, an
266    * exception should be thrown
267    * 
268    * @param r1 IndexReader to perform tests on
269    * @throws Exception
270    */
271   private void performDefaultTests(IndexReader r1) throws Exception {
272     float norm1 = Similarity.getDefault().decodeNormValue(r1.norms("field1")[4]);
273
274     IndexReader pr1Clone = (IndexReader) r1.clone();
275     pr1Clone.deleteDocument(10);
276     pr1Clone.setNorm(4, "field1", 0.5f);
277     assertTrue(Similarity.getDefault().decodeNormValue(r1.norms("field1")[4]) == norm1);
278     assertTrue(Similarity.getDefault().decodeNormValue(pr1Clone.norms("field1")[4]) != norm1);
279
280     assertTrue(!r1.isDeleted(10));
281     assertTrue(pr1Clone.isDeleted(10));
282
283     // try to update the original reader, which should throw an exception
284     try {
285       r1.deleteDocument(11);
286       fail("Tried to delete doc 11 and an exception should have been thrown");
287     } catch (Exception exception) {
288       // expectted
289     }
290     pr1Clone.close();
291   }
292
293   public void testMixedReaders() throws Exception {
294     final Directory dir1 = newDirectory();
295     TestIndexReaderReopen.createIndex(random, dir1, true);
296     final Directory dir2 = newDirectory();
297     TestIndexReaderReopen.createIndex(random, dir2, true);
298     IndexReader r1 = IndexReader.open(dir1, false);
299     IndexReader r2 = IndexReader.open(dir2, false);
300
301     MultiReader multiReader = new MultiReader(new IndexReader[] { r1, r2 });
302     performDefaultTests(multiReader);
303     multiReader.close();
304     dir1.close();
305     dir2.close();
306   }
307
308   public void testSegmentReaderUndeleteall() throws Exception {
309     final Directory dir1 = newDirectory();
310     TestIndexReaderReopen.createIndex(random, dir1, false);
311     SegmentReader origSegmentReader = SegmentReader.getOnlySegmentReader(dir1);
312     origSegmentReader.deleteDocument(10);
313     assertDelDocsRefCountEquals(1, origSegmentReader);
314     origSegmentReader.undeleteAll();
315     assertNull(origSegmentReader.deletedDocsRef);
316     origSegmentReader.close();
317     // need to test norms?
318     dir1.close();
319   }
320   
321   public void testSegmentReaderCloseReferencing() throws Exception {
322     final Directory dir1 = newDirectory();
323     TestIndexReaderReopen.createIndex(random, dir1, false);
324     SegmentReader origSegmentReader = SegmentReader.getOnlySegmentReader(dir1);
325     origSegmentReader.deleteDocument(1);
326     origSegmentReader.setNorm(4, "field1", 0.5f);
327
328     SegmentReader clonedSegmentReader = (SegmentReader) origSegmentReader
329         .clone();
330     assertDelDocsRefCountEquals(2, origSegmentReader);
331     origSegmentReader.close();
332     assertDelDocsRefCountEquals(1, origSegmentReader);
333     // check the norm refs
334     SegmentNorms norm = clonedSegmentReader.norms.get("field1");
335     assertEquals(1, norm.bytesRef().get());
336     clonedSegmentReader.close();
337     dir1.close();
338   }
339   
340   public void testSegmentReaderDelDocsReferenceCounting() throws Exception {
341     final Directory dir1 = newDirectory();
342     TestIndexReaderReopen.createIndex(random, dir1, false);
343
344     IndexReader origReader = IndexReader.open(dir1, false);
345     SegmentReader origSegmentReader = SegmentReader.getOnlySegmentReader(origReader);
346     // deletedDocsRef should be null because nothing has updated yet
347     assertNull(origSegmentReader.deletedDocsRef);
348
349     // we deleted a document, so there is now a deletedDocs bitvector and a
350     // reference to it
351     origReader.deleteDocument(1);
352     assertDelDocsRefCountEquals(1, origSegmentReader);
353
354     // the cloned segmentreader should have 2 references, 1 to itself, and 1 to
355     // the original segmentreader
356     IndexReader clonedReader = (IndexReader) origReader.clone();
357     SegmentReader clonedSegmentReader = SegmentReader.getOnlySegmentReader(clonedReader);
358     assertDelDocsRefCountEquals(2, origSegmentReader);
359     // deleting a document creates a new deletedDocs bitvector, the refs goes to
360     // 1
361     clonedReader.deleteDocument(2);
362     assertDelDocsRefCountEquals(1, origSegmentReader);
363     assertDelDocsRefCountEquals(1, clonedSegmentReader);
364
365     // make sure the deletedocs objects are different (copy
366     // on write)
367     assertTrue(origSegmentReader.deletedDocs != clonedSegmentReader.deletedDocs);
368
369     assertDocDeleted(origSegmentReader, clonedSegmentReader, 1);
370     assertTrue(!origSegmentReader.isDeleted(2)); // doc 2 should not be deleted
371                                                   // in original segmentreader
372     assertTrue(clonedSegmentReader.isDeleted(2)); // doc 2 should be deleted in
373                                                   // cloned segmentreader
374
375     // deleting a doc from the original segmentreader should throw an exception
376     try {
377       origReader.deleteDocument(4);
378       fail("expected exception");
379     } catch (LockObtainFailedException lbfe) {
380       // expected
381     }
382
383     origReader.close();
384     // try closing the original segment reader to see if it affects the
385     // clonedSegmentReader
386     clonedReader.deleteDocument(3);
387     clonedReader.flush();
388     assertDelDocsRefCountEquals(1, clonedSegmentReader);
389
390     // test a reopened reader
391     IndexReader reopenedReader = IndexReader.openIfChanged(clonedReader);
392     if (reopenedReader == null) {
393       reopenedReader = clonedReader;
394     }
395     IndexReader cloneReader2 = (IndexReader) reopenedReader.clone();
396     SegmentReader cloneSegmentReader2 = SegmentReader.getOnlySegmentReader(cloneReader2);
397     assertDelDocsRefCountEquals(2, cloneSegmentReader2);
398     clonedReader.close();
399     reopenedReader.close();
400     cloneReader2.close();
401
402     dir1.close();
403   }
404
405   // LUCENE-1648
406   public void testCloneWithDeletes() throws Throwable {
407     final Directory dir1 = newDirectory();
408     TestIndexReaderReopen.createIndex(random, dir1, false);
409     IndexReader origReader = IndexReader.open(dir1, false);
410     origReader.deleteDocument(1);
411
412     IndexReader clonedReader = (IndexReader) origReader.clone();
413     origReader.close();
414     clonedReader.close();
415
416     IndexReader r = IndexReader.open(dir1, false);
417     assertTrue(r.isDeleted(1));
418     r.close();
419     dir1.close();
420   }
421
422   // LUCENE-1648
423   public void testCloneWithSetNorm() throws Throwable {
424     final Directory dir1 = newDirectory();
425     TestIndexReaderReopen.createIndex(random, dir1, false);
426     IndexReader orig = IndexReader.open(dir1, false);
427     orig.setNorm(1, "field1", 17.0f);
428     final byte encoded = Similarity.getDefault().encodeNormValue(17.0f);
429     assertEquals(encoded, orig.norms("field1")[1]);
430
431     // the cloned segmentreader should have 2 references, 1 to itself, and 1 to
432     // the original segmentreader
433     IndexReader clonedReader = (IndexReader) orig.clone();
434     orig.close();
435     clonedReader.close();
436
437     IndexReader r = IndexReader.open(dir1, false);
438     assertEquals(encoded, r.norms("field1")[1]);
439     r.close();
440     dir1.close();
441   }
442
443   private void assertDocDeleted(SegmentReader reader, SegmentReader reader2,
444       int doc) {
445     assertEquals(reader.isDeleted(doc), reader2.isDeleted(doc));
446   }
447
448   private void assertDelDocsRefCountEquals(int refCount, SegmentReader reader) {
449     assertEquals(refCount, reader.deletedDocsRef.get());
450   }
451   
452   public void testCloneSubreaders() throws Exception {
453     final Directory dir1 = newDirectory();
454  
455     TestIndexReaderReopen.createIndex(random, dir1, true);
456     IndexReader reader = IndexReader.open(dir1, false);
457     reader.deleteDocument(1); // acquire write lock
458     IndexReader[] subs = reader.getSequentialSubReaders();
459     assert subs.length > 1;
460     
461     IndexReader[] clones = new IndexReader[subs.length];
462     for (int x=0; x < subs.length; x++) {
463       clones[x] = (IndexReader) subs[x].clone();
464     }
465     reader.close();
466     for (int x=0; x < subs.length; x++) {
467       clones[x].close();
468     }
469     dir1.close();
470   }
471
472   public void testLucene1516Bug() throws Exception {
473     final Directory dir1 = newDirectory();
474     TestIndexReaderReopen.createIndex(random, dir1, false);
475     IndexReader r1 = IndexReader.open(dir1, false);
476     r1.incRef();
477     IndexReader r2 = r1.clone(false);
478     r1.deleteDocument(5);
479     r1.decRef();
480     
481     r1.incRef();
482     
483     r2.close();
484     r1.decRef();
485     r1.close();
486     dir1.close();
487   }
488
489   public void testCloseStoredFields() throws Exception {
490     final Directory dir = newDirectory();
491     IndexWriter w = new IndexWriter(
492         dir,
493         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
494             setMergePolicy(newLogMergePolicy(false))
495     );
496     Document doc = new Document();
497     doc.add(newField("field", "yes it's stored", Field.Store.YES, Field.Index.ANALYZED));
498     w.addDocument(doc);
499     w.close();
500     IndexReader r1 = IndexReader.open(dir, false);
501     IndexReader r2 = r1.clone(false);
502     r1.close();
503     r2.close();
504     dir.close();
505   }
506 }