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 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;
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
33
public class TestIndexReaderClone extends LuceneTestCase {
35
public void testCloneReadOnlySegmentReader() throws Exception {
36
final Directory dir1 = newDirectory();
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");
44
if (deleteWorked(1, readOnlyReader)) {
45
fail("deleting from the original should not have worked");
48
readOnlyReader.close();
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();
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");
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();
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");
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();
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");
95
// should fail because reader1 holds the write lock
96
assertTrue("first reader should not be able to delete", !deleteWorked(1, r1));
98
// should fail because we are now stale (reader1
100
assertTrue("first reader should not be able to delete", !deleteWorked(1, r1));
106
// create single-segment index, open non-readOnly
107
// SegmentReader, add docs, reopen to multireader, then do
109
public void testReopenSegmentReaderToMultiReader() throws Exception {
110
final Directory dir1 = newDirectory();
112
TestIndexReaderReopen.createIndex(random, dir1, false);
113
IndexReader reader1 = IndexReader.open(dir1, false);
115
TestIndexReaderReopen.modifyIndex(5, dir1);
117
IndexReader reader2 = IndexReader.openIfChanged(reader1);
118
assertNotNull(reader2);
119
assertTrue(reader1 != reader2);
121
assertTrue(deleteWorked(1, reader2));
127
// open non-readOnly reader1, clone to readOnly reader2
128
public void testCloneWriteableToReadOnly() throws Exception {
129
final Directory dir1 = newDirectory();
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");
137
if (deleteWorked(1, readOnlyReader)) {
138
fail("deleting from the original should not have worked");
140
// this readonly reader shouldn't have a write lock
141
if (readOnlyReader.hasChanges) {
142
fail("readOnlyReader has a write lock");
145
readOnlyReader.close();
149
// open non-readOnly reader1, reopen to readOnly reader2
150
public void testReopenWriteableToReadOnly() throws Exception {
151
final Directory dir1 = newDirectory();
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());
159
IndexReader readOnlyReader = IndexReader.openIfChanged(reader, true);
160
assertNotNull(readOnlyReader);
161
if (!isReadOnly(readOnlyReader)) {
162
fail("reader isn't read only");
164
assertFalse(deleteWorked(1, readOnlyReader));
165
assertEquals(docCount-1, readOnlyReader.numDocs());
167
readOnlyReader.close();
171
// open readOnly reader1, clone to non-readOnly reader2
172
public void testCloneReadOnlyToWriteable() throws Exception {
173
final Directory dir1 = newDirectory();
175
TestIndexReaderReopen.createIndex(random, dir1, true);
176
IndexReader reader1 = IndexReader.open(dir1, true);
178
IndexReader reader2 = reader1.clone(false);
179
if (isReadOnly(reader2)) {
180
fail("reader should not be read only");
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");
187
assertTrue("deleting from the cloned reader should have worked", deleteWorked(1, reader2));
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();
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)));
204
IndexReader reader2 = reader1.clone(true);
205
assertTrue(isReadOnly(reader2));
211
private static boolean deleteWorked(int doc, IndexReader r) {
212
boolean exception = false;
214
// trying to delete from the original reader should throw an exception
215
r.deleteDocument(doc);
216
} catch (Exception ex) {
222
public void testCloneReadOnlyDirectoryReader() throws Exception {
223
final Directory dir1 = newDirectory();
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");
232
readOnlyReader.close();
236
public static boolean isReadOnly(IndexReader r) {
237
if (r instanceof ReadOnlySegmentReader
238
|| r instanceof ReadOnlyDirectoryReader)
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);
251
ParallelReader pr1 = new ParallelReader();
255
performDefaultTests(pr1);
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
268
* @param r1 IndexReader to perform tests on
271
private void performDefaultTests(IndexReader r1) throws Exception {
272
float norm1 = Similarity.getDefault().decodeNormValue(r1.norms("field1")[4]);
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);
280
assertTrue(!r1.isDeleted(10));
281
assertTrue(pr1Clone.isDeleted(10));
283
// try to update the original reader, which should throw an exception
285
r1.deleteDocument(11);
286
fail("Tried to delete doc 11 and an exception should have been thrown");
287
} catch (Exception exception) {
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);
301
MultiReader multiReader = new MultiReader(new IndexReader[] { r1, r2 });
302
performDefaultTests(multiReader);
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?
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);
328
SegmentReader clonedSegmentReader = (SegmentReader) origSegmentReader
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();
340
public void testSegmentReaderDelDocsReferenceCounting() throws Exception {
341
final Directory dir1 = newDirectory();
342
TestIndexReaderReopen.createIndex(random, dir1, false);
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);
349
// we deleted a document, so there is now a deletedDocs bitvector and a
351
origReader.deleteDocument(1);
352
assertDelDocsRefCountEquals(1, origSegmentReader);
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
361
clonedReader.deleteDocument(2);
362
assertDelDocsRefCountEquals(1, origSegmentReader);
363
assertDelDocsRefCountEquals(1, clonedSegmentReader);
365
// make sure the deletedocs objects are different (copy
367
assertTrue(origSegmentReader.deletedDocs != clonedSegmentReader.deletedDocs);
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
375
// deleting a doc from the original segmentreader should throw an exception
377
origReader.deleteDocument(4);
378
fail("expected exception");
379
} catch (LockObtainFailedException lbfe) {
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);
390
// test a reopened reader
391
IndexReader reopenedReader = IndexReader.openIfChanged(clonedReader);
392
if (reopenedReader == null) {
393
reopenedReader = clonedReader;
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();
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);
412
IndexReader clonedReader = (IndexReader) origReader.clone();
414
clonedReader.close();
416
IndexReader r = IndexReader.open(dir1, false);
417
assertTrue(r.isDeleted(1));
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]);
431
// the cloned segmentreader should have 2 references, 1 to itself, and 1 to
432
// the original segmentreader
433
IndexReader clonedReader = (IndexReader) orig.clone();
435
clonedReader.close();
437
IndexReader r = IndexReader.open(dir1, false);
438
assertEquals(encoded, r.norms("field1")[1]);
443
private void assertDocDeleted(SegmentReader reader, SegmentReader reader2,
445
assertEquals(reader.isDeleted(doc), reader2.isDeleted(doc));
448
private void assertDelDocsRefCountEquals(int refCount, SegmentReader reader) {
449
assertEquals(refCount, reader.deletedDocsRef.get());
452
public void testCloneSubreaders() throws Exception {
453
final Directory dir1 = newDirectory();
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;
461
IndexReader[] clones = new IndexReader[subs.length];
462
for (int x=0; x < subs.length; x++) {
463
clones[x] = (IndexReader) subs[x].clone();
466
for (int x=0; x < subs.length; x++) {
472
public void testLucene1516Bug() throws Exception {
473
final Directory dir1 = newDirectory();
474
TestIndexReaderReopen.createIndex(random, dir1, false);
475
IndexReader r1 = IndexReader.open(dir1, false);
477
IndexReader r2 = r1.clone(false);
478
r1.deleteDocument(5);
489
public void testCloseStoredFields() throws Exception {
490
final Directory dir = newDirectory();
491
IndexWriter w = new IndexWriter(
493
newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
494
setMergePolicy(newLogMergePolicy(false))
496
Document doc = new Document();
497
doc.add(newField("field", "yes it's stored", Field.Store.YES, Field.Index.ANALYZED));
500
IndexReader r1 = IndexReader.open(dir, false);
501
IndexReader r2 = r1.clone(false);