blob: cb21a3f1fce681d57875bf85ecb5f116a0c3dc65 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using NUnit.Framework;
using SimpleAnalyzer = Lucene.Net.Analysis.SimpleAnalyzer;
using Document = Lucene.Net.Documents.Document;
using Field = Lucene.Net.Documents.Field;
using Norm = Lucene.Net.Index.SegmentReader.Norm;
using AlreadyClosedException = Lucene.Net.Store.AlreadyClosedException;
using Directory = Lucene.Net.Store.Directory;
using FSDirectory = Lucene.Net.Store.FSDirectory;
using LockObtainFailedException = Lucene.Net.Store.LockObtainFailedException;
using MockRAMDirectory = Lucene.Net.Store.MockRAMDirectory;
using Similarity = Lucene.Net.Search.Similarity;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
namespace Lucene.Net.Index
{
/// <summary> Tests cloning multiple types of readers, modifying the deletedDocs and norms
/// and verifies copy on write semantics of the deletedDocs and norms is
/// implemented properly
/// </summary>
[TestFixture]
public class TestIndexReaderClone:LuceneTestCase
{
[Test]
public virtual void TestCloneReadOnlySegmentReader()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader reader = IndexReader.Open(dir1, false);
IndexReader readOnlyReader = reader.Clone(true);
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
Assert.IsFalse(DeleteWorked(1, readOnlyReader), "deleting from the original should not have worked");
reader.Close();
readOnlyReader.Close();
dir1.Close();
}
// open non-readOnly reader1, clone to non-readOnly
// reader2, make sure we can change reader2
[Test]
public virtual void TestCloneNoChangesStillReadOnly()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader r1 = IndexReader.Open(dir1, false);
IndexReader r2 = r1.Clone(false);
Assert.IsTrue(DeleteWorked(1, r2), "deleting from the cloned should have worked");
r1.Close();
r2.Close();
dir1.Close();
}
// open non-readOnly reader1, clone to non-readOnly
// reader2, make sure we can change reader1
[Test]
public virtual void TestCloneWriteToOrig()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader r1 = IndexReader.Open(dir1, false);
IndexReader r2 = r1.Clone(false);
Assert.IsTrue(DeleteWorked(1, r1), "deleting from the original should have worked");
r1.Close();
r2.Close();
dir1.Close();
}
// open non-readOnly reader1, clone to non-readOnly
// reader2, make sure we can change reader2
[Test]
public virtual void TestCloneWriteToClone()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader r1 = IndexReader.Open(dir1, false);
IndexReader r2 = r1.Clone(false);
Assert.IsTrue(DeleteWorked(1, r2), "deleting from the original should have worked");
// should fail because reader1 holds the write lock
Assert.IsTrue(!DeleteWorked(1, r1), "first reader should not be able to delete");
r2.Close();
// should fail because we are now stale (reader1
// committed changes)
Assert.IsTrue(!DeleteWorked(1, r1), "first reader should not be able to delete");
r1.Close();
dir1.Close();
}
// create single-segment index, open non-readOnly
// SegmentReader, add docs, reopen to multireader, then do
// delete
[Test]
public virtual void TestReopenSegmentReaderToMultiReader()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader reader1 = IndexReader.Open(dir1, false);
TestIndexReaderReopen.ModifyIndex(5, dir1);
IndexReader reader2 = reader1.Reopen();
Assert.IsTrue(reader1 != reader2);
Assert.IsTrue(DeleteWorked(1, reader2));
reader1.Close();
reader2.Close();
dir1.Close();
}
// open non-readOnly reader1, clone to readOnly reader2
[Test]
public virtual void TestCloneWriteableToReadOnly()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader = IndexReader.Open(dir1, false);
IndexReader readOnlyReader = reader.Clone(true);
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
Assert.IsFalse(DeleteWorked(1, readOnlyReader), "deleting from the original should not have worked");
// this readonly reader shouldn't have a write lock
Assert.IsFalse(readOnlyReader.hasChanges, "readOnlyReader has a write lock");
reader.Close();
readOnlyReader.Close();
dir1.Close();
}
// open non-readOnly reader1, reopen to readOnly reader2
[Test]
public virtual void TestReopenWriteableToReadOnly()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader = IndexReader.Open(dir1, false);
int docCount = reader.NumDocs();
Assert.IsTrue(DeleteWorked(1, reader));
Assert.AreEqual(docCount - 1, reader.NumDocs());
IndexReader readOnlyReader = reader.Reopen(true);
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
Assert.IsFalse(DeleteWorked(1, readOnlyReader));
Assert.AreEqual(docCount - 1, readOnlyReader.NumDocs());
reader.Close();
readOnlyReader.Close();
dir1.Close();
}
// open readOnly reader1, clone to non-readOnly reader2
[Test]
public virtual void TestCloneReadOnlyToWriteable()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader1 = IndexReader.Open(dir1, true);
IndexReader reader2 = reader1.Clone(false);
Assert.IsFalse(IsReadOnly(reader2), "reader should not be read only");
Assert.IsFalse(DeleteWorked(1, reader1), "deleting from the original reader should not have worked");
// this readonly reader shouldn't yet have a write lock
Assert.IsFalse(reader2.hasChanges, "cloned reader should not have write lock");
Assert.IsTrue(DeleteWorked(1, reader2), "deleting from the cloned reader should have worked");
reader1.Close();
reader2.Close();
dir1.Close();
}
// open non-readOnly reader1 on multi-segment index, then
// optimize the index, then clone to readOnly reader2
[Test]
public virtual void TestReadOnlyCloneAfterOptimize()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader1 = IndexReader.Open(dir1, false);
IndexWriter w = new IndexWriter(dir1, new SimpleAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
w.Optimize();
w.Close();
IndexReader reader2 = reader1.Clone(true);
Assert.IsTrue(IsReadOnly(reader2));
reader1.Close();
reader2.Close();
dir1.Close();
}
private static bool DeleteWorked(int doc, IndexReader r)
{
bool exception = false;
try
{
// trying to delete from the original reader should throw an exception
r.DeleteDocument(doc);
}
catch (System.Exception ex)
{
exception = true;
}
return !exception;
}
[Test]
public virtual void TestCloneReadOnlyDirectoryReader()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader = IndexReader.Open(dir1, false);
IndexReader readOnlyReader = reader.Clone(true);
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
reader.Close();
readOnlyReader.Close();
dir1.Close();
}
public static bool IsReadOnly(IndexReader r)
{
if (r is ReadOnlySegmentReader || r is ReadOnlyDirectoryReader)
return true;
return false;
}
[Test]
public virtual void TestParallelReader()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
Directory dir2 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir2, true);
IndexReader r1 = IndexReader.Open(dir1, false);
IndexReader r2 = IndexReader.Open(dir2, false);
ParallelReader pr1 = new ParallelReader();
pr1.Add(r1);
pr1.Add(r2);
PerformDefaultTests(pr1);
pr1.Close();
dir1.Close();
dir2.Close();
}
/// <summary> 1. Get a norm from the original reader 2. Clone the original reader 3.
/// Delete a document and set the norm of the cloned reader 4. Verify the norms
/// are not the same on each reader 5. Verify the doc deleted is only in the
/// cloned reader 6. Try to delete a document in the original reader, an
/// exception should be thrown
///
/// </summary>
/// <param name="r1">IndexReader to perform tests on
/// </param>
/// <throws> Exception </throws>
private void PerformDefaultTests(IndexReader r1)
{
float norm1 = Similarity.DecodeNorm(r1.Norms("field1")[4]);
IndexReader pr1Clone = (IndexReader) r1.Clone();
pr1Clone.DeleteDocument(10);
pr1Clone.SetNorm(4, "field1", 0.5f);
Assert.IsTrue(Similarity.DecodeNorm(r1.Norms("field1")[4]) == norm1);
Assert.IsTrue(Similarity.DecodeNorm(pr1Clone.Norms("field1")[4]) != norm1);
Assert.IsTrue(!r1.IsDeleted(10));
Assert.IsTrue(pr1Clone.IsDeleted(10));
// try to update the original reader, which should throw an exception
Assert.Throws<LockObtainFailedException>(() => r1.DeleteDocument(11),
"Tried to delete doc 11 and an exception should have been thrown");
pr1Clone.Close();
}
[Test]
public virtual void TestMixedReaders()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
Directory dir2 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir2, true);
IndexReader r1 = IndexReader.Open(dir1, false);
IndexReader r2 = IndexReader.Open(dir2, false);
MultiReader multiReader = new MultiReader(new IndexReader[]{r1, r2});
PerformDefaultTests(multiReader);
multiReader.Close();
dir1.Close();
dir2.Close();
}
[Test]
public virtual void TestSegmentReaderUndeleteall()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(dir1);
origSegmentReader.DeleteDocument(10);
AssertDelDocsRefCountEquals(1, origSegmentReader);
origSegmentReader.UndeleteAll();
Assert.IsNull(origSegmentReader.deletedDocsRef_ForNUnit);
origSegmentReader.Close();
// need to test norms?
dir1.Close();
}
[Test]
public virtual void TestSegmentReaderCloseReferencing()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(dir1);
origSegmentReader.DeleteDocument(1);
origSegmentReader.SetNorm(4, "field1", 0.5f);
SegmentReader clonedSegmentReader = (SegmentReader) origSegmentReader.Clone();
AssertDelDocsRefCountEquals(2, origSegmentReader);
origSegmentReader.Close();
AssertDelDocsRefCountEquals(1, origSegmentReader);
// check the norm refs
Norm norm = clonedSegmentReader.norms_ForNUnit["field1"];
Assert.AreEqual(1, norm.BytesRef().RefCount());
clonedSegmentReader.Close();
dir1.Close();
}
[Test]
public virtual void TestSegmentReaderDelDocsReferenceCounting()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader origReader = IndexReader.Open(dir1, false);
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(origReader);
// deletedDocsRef should be null because nothing has updated yet
Assert.IsNull(origSegmentReader.deletedDocsRef_ForNUnit);
// we deleted a document, so there is now a deletedDocs bitvector and a
// reference to it
origReader.DeleteDocument(1);
AssertDelDocsRefCountEquals(1, origSegmentReader);
// the cloned segmentreader should have 2 references, 1 to itself, and 1 to
// the original segmentreader
IndexReader clonedReader = (IndexReader) origReader.Clone();
SegmentReader clonedSegmentReader = SegmentReader.GetOnlySegmentReader(clonedReader);
AssertDelDocsRefCountEquals(2, origSegmentReader);
// deleting a document creates a new deletedDocs bitvector, the refs goes to
// 1
clonedReader.DeleteDocument(2);
AssertDelDocsRefCountEquals(1, origSegmentReader);
AssertDelDocsRefCountEquals(1, clonedSegmentReader);
// make sure the deletedocs objects are different (copy
// on write)
Assert.IsTrue(origSegmentReader.deletedDocs_ForNUnit != clonedSegmentReader.deletedDocs_ForNUnit);
AssertDocDeleted(origSegmentReader, clonedSegmentReader, 1);
Assert.IsTrue(!origSegmentReader.IsDeleted(2)); // doc 2 should not be deleted
// in original segmentreader
Assert.IsTrue(clonedSegmentReader.IsDeleted(2)); // doc 2 should be deleted in
// cloned segmentreader
// deleting a doc from the original segmentreader should throw an exception
Assert.Throws<LockObtainFailedException>(() => origReader.DeleteDocument(4), "expected exception");
origReader.Close();
// try closing the original segment reader to see if it affects the
// clonedSegmentReader
clonedReader.DeleteDocument(3);
clonedReader.Flush();
AssertDelDocsRefCountEquals(1, clonedSegmentReader);
// test a reopened reader
IndexReader reopenedReader = clonedReader.Reopen();
IndexReader cloneReader2 = (IndexReader) reopenedReader.Clone();
SegmentReader cloneSegmentReader2 = SegmentReader.GetOnlySegmentReader(cloneReader2);
AssertDelDocsRefCountEquals(2, cloneSegmentReader2);
clonedReader.Close();
reopenedReader.Close();
cloneReader2.Close();
dir1.Close();
}
// LUCENE-1648
[Test]
public virtual void TestCloneWithDeletes()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader origReader = IndexReader.Open(dir1, false);
origReader.DeleteDocument(1);
IndexReader clonedReader = (IndexReader) origReader.Clone();
origReader.Close();
clonedReader.Close();
IndexReader r = IndexReader.Open(dir1, false);
Assert.IsTrue(r.IsDeleted(1));
r.Close();
dir1.Close();
}
// LUCENE-1648
[Test]
public virtual void TestCloneWithSetNorm()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader orig = IndexReader.Open(dir1, false);
orig.SetNorm(1, "field1", 17.0f);
byte encoded = Similarity.EncodeNorm(17.0f);
Assert.AreEqual(encoded, orig.Norms("field1")[1]);
// the cloned segmentreader should have 2 references, 1 to itself, and 1 to
// the original segmentreader
IndexReader clonedReader = (IndexReader) orig.Clone();
orig.Close();
clonedReader.Close();
IndexReader r = IndexReader.Open(dir1, false);
Assert.AreEqual(encoded, r.Norms("field1")[1]);
r.Close();
dir1.Close();
}
private void AssertDocDeleted(SegmentReader reader, SegmentReader reader2, int doc)
{
Assert.AreEqual(reader.IsDeleted(doc), reader2.IsDeleted(doc));
}
private void AssertDelDocsRefCountEquals(int refCount, SegmentReader reader)
{
Assert.AreEqual(refCount, reader.deletedDocsRef_ForNUnit.RefCount());
}
[Test]
public virtual void TestCloneSubreaders()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, true);
IndexReader reader = IndexReader.Open(dir1, false);
reader.DeleteDocument(1); // acquire write lock
IndexReader[] subs = reader.GetSequentialSubReaders();
System.Diagnostics.Debug.Assert(subs.Length > 1);
IndexReader[] clones = new IndexReader[subs.Length];
for (int x = 0; x < subs.Length; x++)
{
clones[x] = (IndexReader) subs[x].Clone();
}
reader.Close();
for (int x = 0; x < subs.Length; x++)
{
clones[x].Close();
}
dir1.Close();
}
[Test]
public virtual void TestLucene1516Bug()
{
Directory dir1 = new MockRAMDirectory();
TestIndexReaderReopen.CreateIndex(dir1, false);
IndexReader r1 = IndexReader.Open(dir1, false);
r1.IncRef();
IndexReader r2 = r1.Clone(false);
r1.DeleteDocument(5);
r1.DecRef();
r1.IncRef();
r2.Close();
r1.DecRef();
r1.Close();
dir1.Close();
}
[Test]
public virtual void TestCloseStoredFields()
{
Directory dir = new MockRAMDirectory();
IndexWriter w = new IndexWriter(dir, new SimpleAnalyzer(), IndexWriter.MaxFieldLength.UNLIMITED);
w.UseCompoundFile = false;
Document doc = new Document();
doc.Add(new Field("field", "yes it's stored", Field.Store.YES, Field.Index.ANALYZED));
w.AddDocument(doc);
w.Close();
IndexReader r1 = IndexReader.Open(dir, false);
IndexReader r2 = r1.Clone(false);
r1.Close();
r2.Close();
dir.Close();
}
}
}