blob: 44111033fcd79dabc48b0321d9871b3e7bab0ec2 [file] [log] [blame]
using J2N.Threading;
using Lucene.Net.Attributes;
using Lucene.Net.Codecs;
using Lucene.Net.Documents;
using Lucene.Net.Index.Extensions;
using Lucene.Net.Support;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Assert = Lucene.Net.TestFramework.Assert;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Index
{
/*
* 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 BaseDirectoryWrapper = Lucene.Net.Store.BaseDirectoryWrapper;
using Codec = Lucene.Net.Codecs.Codec;
using Directory = Lucene.Net.Store.Directory;
using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
using Document = Documents.Document;
using Field = Field;
using FieldType = FieldType;
using FilterCodec = Lucene.Net.Codecs.FilterCodec;
using IOUtils = Lucene.Net.Util.IOUtils;
using LockObtainFailedException = Lucene.Net.Store.LockObtainFailedException;
using Lucene46Codec = Lucene.Net.Codecs.Lucene46.Lucene46Codec;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
using MockDirectoryWrapper = Lucene.Net.Store.MockDirectoryWrapper;
using PhraseQuery = Lucene.Net.Search.PhraseQuery;
using PostingsFormat = Lucene.Net.Codecs.PostingsFormat;
using Pulsing41PostingsFormat = Lucene.Net.Codecs.Pulsing.Pulsing41PostingsFormat;
using RAMDirectory = Lucene.Net.Store.RAMDirectory;
using StringField = StringField;
using TestUtil = Lucene.Net.Util.TestUtil;
using TextField = TextField;
[TestFixture]
public class TestAddIndexes : LuceneTestCase
{
[Test]
public virtual void TestSimpleCase()
{
// main directory
Directory dir = NewDirectory();
// two auxiliary directories
Directory aux = NewDirectory();
Directory aux2 = NewDirectory();
IndexWriter writer = null;
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE));
// add 100 documents
AddDocs(writer, 100);
Assert.AreEqual(100, writer.MaxDoc);
writer.Dispose();
TestUtil.CheckIndex(dir);
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMergePolicy(NewLogMergePolicy(false)));
// add 40 documents in separate files
AddDocs(writer, 40);
Assert.AreEqual(40, writer.MaxDoc);
writer.Dispose();
writer = NewWriter(aux2, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE));
// add 50 documents in compound files
AddDocs2(writer, 50);
Assert.AreEqual(50, writer.MaxDoc);
writer.Dispose();
// test doc count before segments are merged
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
Assert.AreEqual(100, writer.MaxDoc);
writer.AddIndexes(aux, aux2);
Assert.AreEqual(190, writer.MaxDoc);
writer.Dispose();
TestUtil.CheckIndex(dir);
// make sure the old index is correct
VerifyNumDocs(aux, 40);
// make sure the new index is correct
VerifyNumDocs(dir, 190);
// now add another set in.
Directory aux3 = NewDirectory();
writer = NewWriter(aux3, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
// add 40 documents
AddDocs(writer, 40);
Assert.AreEqual(40, writer.MaxDoc);
writer.Dispose();
// test doc count before segments are merged
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
Assert.AreEqual(190, writer.MaxDoc);
writer.AddIndexes(aux3);
Assert.AreEqual(230, writer.MaxDoc);
writer.Dispose();
// make sure the new index is correct
VerifyNumDocs(dir, 230);
VerifyTermDocs(dir, new Term("content", "aaa"), 180);
VerifyTermDocs(dir, new Term("content", "bbb"), 50);
// now fully merge it.
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
writer.ForceMerge(1);
writer.Dispose();
// make sure the new index is correct
VerifyNumDocs(dir, 230);
VerifyTermDocs(dir, new Term("content", "aaa"), 180);
VerifyTermDocs(dir, new Term("content", "bbb"), 50);
// now add a single document
Directory aux4 = NewDirectory();
writer = NewWriter(aux4, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
AddDocs2(writer, 1);
writer.Dispose();
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
Assert.AreEqual(230, writer.MaxDoc);
writer.AddIndexes(aux4);
Assert.AreEqual(231, writer.MaxDoc);
writer.Dispose();
VerifyNumDocs(dir, 231);
VerifyTermDocs(dir, new Term("content", "bbb"), 51);
dir.Dispose();
aux.Dispose();
aux2.Dispose();
aux3.Dispose();
aux4.Dispose();
}
[Test]
public virtual void TestWithPendingDeletes()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
writer.AddIndexes(aux);
// Adds 10 docs, then replaces them with another 10
// docs, so 10 pending deletes:
for (int i = 0; i < 20; i++)
{
Document doc = new Document();
doc.Add(NewStringField("id", "" + (i % 10), Field.Store.NO));
doc.Add(NewTextField("content", "bbb " + i, Field.Store.NO));
writer.UpdateDocument(new Term("id", "" + (i % 10)), doc);
}
// Deletes one of the 10 added docs, leaving 9:
PhraseQuery q = new PhraseQuery();
q.Add(new Term("content", "bbb"));
q.Add(new Term("content", "14"));
writer.DeleteDocuments(q);
writer.ForceMerge(1);
writer.Commit();
VerifyNumDocs(dir, 1039);
VerifyTermDocs(dir, new Term("content", "aaa"), 1030);
VerifyTermDocs(dir, new Term("content", "bbb"), 9);
writer.Dispose();
dir.Dispose();
aux.Dispose();
}
[Test]
public virtual void TestWithPendingDeletes2()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
// Adds 10 docs, then replaces them with another 10
// docs, so 10 pending deletes:
for (int i = 0; i < 20; i++)
{
Document doc = new Document();
doc.Add(NewStringField("id", "" + (i % 10), Field.Store.NO));
doc.Add(NewTextField("content", "bbb " + i, Field.Store.NO));
writer.UpdateDocument(new Term("id", "" + (i % 10)), doc);
}
writer.AddIndexes(aux);
// Deletes one of the 10 added docs, leaving 9:
PhraseQuery q = new PhraseQuery();
q.Add(new Term("content", "bbb"));
q.Add(new Term("content", "14"));
writer.DeleteDocuments(q);
writer.ForceMerge(1);
writer.Commit();
VerifyNumDocs(dir, 1039);
VerifyTermDocs(dir, new Term("content", "aaa"), 1030);
VerifyTermDocs(dir, new Term("content", "bbb"), 9);
writer.Dispose();
dir.Dispose();
aux.Dispose();
}
[Test]
public virtual void TestWithPendingDeletes3()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
// Adds 10 docs, then replaces them with another 10
// docs, so 10 pending deletes:
for (int i = 0; i < 20; i++)
{
Document doc = new Document();
doc.Add(NewStringField("id", "" + (i % 10), Field.Store.NO));
doc.Add(NewTextField("content", "bbb " + i, Field.Store.NO));
writer.UpdateDocument(new Term("id", "" + (i % 10)), doc);
}
// Deletes one of the 10 added docs, leaving 9:
PhraseQuery q = new PhraseQuery();
q.Add(new Term("content", "bbb"));
q.Add(new Term("content", "14"));
writer.DeleteDocuments(q);
writer.AddIndexes(aux);
writer.ForceMerge(1);
writer.Commit();
VerifyNumDocs(dir, 1039);
VerifyTermDocs(dir, new Term("content", "aaa"), 1030);
VerifyTermDocs(dir, new Term("content", "bbb"), 9);
writer.Dispose();
dir.Dispose();
aux.Dispose();
}
// case 0: add self or exceed maxMergeDocs, expect exception
[Test]
public virtual void TestAddSelf()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
IndexWriter writer = null;
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
// add 100 documents
AddDocs(writer, 100);
Assert.AreEqual(100, writer.MaxDoc);
writer.Dispose();
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(1000).SetMergePolicy(NewLogMergePolicy(false)));
// add 140 documents in separate files
AddDocs(writer, 40);
writer.Dispose();
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(1000).SetMergePolicy(NewLogMergePolicy(false)));
AddDocs(writer, 100);
writer.Dispose();
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND));
try
{
// cannot add self
writer.AddIndexes(aux, dir);
Assert.IsTrue(false);
}
#pragma warning disable 168
catch (ArgumentException e)
#pragma warning restore 168
{
Assert.AreEqual(100, writer.MaxDoc);
}
writer.Dispose();
// make sure the index is correct
VerifyNumDocs(dir, 100);
dir.Dispose();
aux.Dispose();
}
// in all the remaining tests, make the doc count of the oldest segment
// in dir large so that it is never merged in addIndexes()
// case 1: no tail segments
[Test]
public virtual void TestNoTailSegments()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(10).SetMergePolicy(NewLogMergePolicy(4)));
AddDocs(writer, 10);
writer.AddIndexes(aux);
Assert.AreEqual(1040, writer.MaxDoc);
Assert.AreEqual(1000, writer.GetDocCount(0));
writer.Dispose();
// make sure the index is correct
VerifyNumDocs(dir, 1040);
dir.Dispose();
aux.Dispose();
}
// case 2: tail segments, invariants hold, no copy
[Test]
public virtual void TestNoCopySegments()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(9).SetMergePolicy(NewLogMergePolicy(4)));
AddDocs(writer, 2);
writer.AddIndexes(aux);
Assert.AreEqual(1032, writer.MaxDoc);
Assert.AreEqual(1000, writer.GetDocCount(0));
writer.Dispose();
// make sure the index is correct
VerifyNumDocs(dir, 1032);
dir.Dispose();
aux.Dispose();
}
// case 3: tail segments, invariants hold, copy, invariants hold
[Test]
public virtual void TestNoMergeAfterCopy()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux);
IndexWriter writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(10).SetMergePolicy(NewLogMergePolicy(4)));
writer.AddIndexes(aux, new MockDirectoryWrapper(Random, new RAMDirectory(aux, NewIOContext(Random))));
Assert.AreEqual(1060, writer.MaxDoc);
Assert.AreEqual(1000, writer.GetDocCount(0));
writer.Dispose();
// make sure the index is correct
VerifyNumDocs(dir, 1060);
dir.Dispose();
aux.Dispose();
}
// case 4: tail segments, invariants hold, copy, invariants not hold
[Test]
public virtual void TestMergeAfterCopy()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
SetUpDirs(dir, aux, true);
IndexWriterConfig dontMergeConfig = (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetMergePolicy(NoMergePolicy.COMPOUND_FILES);
IndexWriter writer = new IndexWriter(aux, dontMergeConfig);
for (int i = 0; i < 20; i++)
{
writer.DeleteDocuments(new Term("id", "" + i));
}
writer.Dispose();
IndexReader reader = DirectoryReader.Open(aux);
Assert.AreEqual(10, reader.NumDocs);
reader.Dispose();
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(4).SetMergePolicy(NewLogMergePolicy(4)));
if (Verbose)
{
Console.WriteLine("\nTEST: now addIndexes");
}
writer.AddIndexes(aux, new MockDirectoryWrapper(Random, new RAMDirectory(aux, NewIOContext(Random))));
Assert.AreEqual(1020, writer.MaxDoc);
Assert.AreEqual(1000, writer.GetDocCount(0));
writer.Dispose();
dir.Dispose();
aux.Dispose();
}
// case 5: tail segments, invariants not hold
[Test]
public virtual void TestMoreMerges()
{
// main directory
Directory dir = NewDirectory();
// auxiliary directory
Directory aux = NewDirectory();
Directory aux2 = NewDirectory();
SetUpDirs(dir, aux, true);
IndexWriter writer = NewWriter(aux2, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(100).SetMergePolicy(NewLogMergePolicy(10)));
writer.AddIndexes(aux);
Assert.AreEqual(30, writer.MaxDoc);
Assert.AreEqual(3, writer.SegmentCount);
writer.Dispose();
IndexWriterConfig dontMergeConfig = (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetMergePolicy(NoMergePolicy.COMPOUND_FILES);
writer = new IndexWriter(aux, dontMergeConfig);
for (int i = 0; i < 27; i++)
{
writer.DeleteDocuments(new Term("id", "" + i));
}
writer.Dispose();
IndexReader reader = DirectoryReader.Open(aux);
Assert.AreEqual(3, reader.NumDocs);
reader.Dispose();
dontMergeConfig = (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetMergePolicy(NoMergePolicy.COMPOUND_FILES);
writer = new IndexWriter(aux2, dontMergeConfig);
for (int i = 0; i < 8; i++)
{
writer.DeleteDocuments(new Term("id", "" + i));
}
writer.Dispose();
reader = DirectoryReader.Open(aux2);
Assert.AreEqual(22, reader.NumDocs);
reader.Dispose();
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(6).SetMergePolicy(NewLogMergePolicy(4)));
writer.AddIndexes(aux, aux2);
Assert.AreEqual(1040, writer.MaxDoc);
Assert.AreEqual(1000, writer.GetDocCount(0));
writer.Dispose();
dir.Dispose();
aux.Dispose();
aux2.Dispose();
}
private IndexWriter NewWriter(Directory dir, IndexWriterConfig conf)
{
conf.SetMergePolicy(new LogDocMergePolicy());
IndexWriter writer = new IndexWriter(dir, conf);
return writer;
}
private void AddDocs(IndexWriter writer, int numDocs)
{
for (int i = 0; i < numDocs; i++)
{
Document doc = new Document();
doc.Add(NewTextField("content", "aaa", Field.Store.NO));
writer.AddDocument(doc);
}
}
private void AddDocs2(IndexWriter writer, int numDocs)
{
for (int i = 0; i < numDocs; i++)
{
Document doc = new Document();
doc.Add(NewTextField("content", "bbb", Field.Store.NO));
writer.AddDocument(doc);
}
}
private void VerifyNumDocs(Directory dir, int numDocs)
{
IndexReader reader = DirectoryReader.Open(dir);
Assert.AreEqual(numDocs, reader.MaxDoc);
Assert.AreEqual(numDocs, reader.NumDocs);
reader.Dispose();
}
private void VerifyTermDocs(Directory dir, Term term, int numDocs)
{
IndexReader reader = DirectoryReader.Open(dir);
DocsEnum docsEnum = TestUtil.Docs(Random, reader, term.Field, term.Bytes, null, null, DocsFlags.NONE);
int count = 0;
while (docsEnum.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
{
count++;
}
Assert.AreEqual(numDocs, count);
reader.Dispose();
}
private void SetUpDirs(Directory dir, Directory aux)
{
SetUpDirs(dir, aux, false);
}
private void SetUpDirs(Directory dir, Directory aux, bool withID)
{
IndexWriter writer = null;
writer = NewWriter(dir, (IndexWriterConfig)NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(1000));
// add 1000 documents in 1 segment
if (withID)
{
AddDocsWithID(writer, 1000, 0);
}
else
{
AddDocs(writer, 1000);
}
Assert.AreEqual(1000, writer.MaxDoc);
Assert.AreEqual(1, writer.SegmentCount);
writer.Dispose();
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(1000).SetMergePolicy(NewLogMergePolicy(false, 10)));
// add 30 documents in 3 segments
for (int i = 0; i < 3; i++)
{
if (withID)
{
AddDocsWithID(writer, 10, 10 * i);
}
else
{
AddDocs(writer, 10);
}
writer.Dispose();
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(1000).SetMergePolicy(NewLogMergePolicy(false, 10)));
}
Assert.AreEqual(30, writer.MaxDoc);
Assert.AreEqual(3, writer.SegmentCount);
writer.Dispose();
}
// LUCENE-1270
[Test]
public virtual void TestHangOnClose()
{
Directory dir = NewDirectory();
LogByteSizeMergePolicy lmp = new LogByteSizeMergePolicy();
lmp.NoCFSRatio = 0.0;
lmp.MergeFactor = 100;
IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMaxBufferedDocs(5).SetMergePolicy(lmp));
Document doc = new Document();
FieldType customType = new FieldType(TextField.TYPE_STORED);
customType.StoreTermVectors = true;
customType.StoreTermVectorPositions = true;
customType.StoreTermVectorOffsets = true;
doc.Add(NewField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", customType));
for (int i = 0; i < 60; i++)
{
writer.AddDocument(doc);
}
Document doc2 = new Document();
FieldType customType2 = new FieldType();
customType2.IsStored = true;
doc2.Add(NewField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", customType2));
doc2.Add(NewField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", customType2));
doc2.Add(NewField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", customType2));
doc2.Add(NewField("content", "aaa bbb ccc ddd eee fff ggg hhh iii", customType2));
for (int i = 0; i < 10; i++)
{
writer.AddDocument(doc2);
}
writer.Dispose();
Directory dir2 = NewDirectory();
lmp = new LogByteSizeMergePolicy();
lmp.MinMergeMB = 0.0001;
lmp.NoCFSRatio = 0.0;
lmp.MergeFactor = 4;
writer = new IndexWriter(dir2, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMergeScheduler(new SerialMergeScheduler()).SetMergePolicy(lmp));
writer.AddIndexes(dir);
writer.Dispose();
dir.Dispose();
dir2.Dispose();
}
// TODO: these are also in TestIndexWriter... add a simple doc-writing method
// like this to LuceneTestCase?
private void AddDoc(IndexWriter writer)
{
Document doc = new Document();
doc.Add(NewTextField("content", "aaa", Field.Store.NO));
writer.AddDocument(doc);
}
private abstract class RunAddIndexesThreads
{
internal Directory dir, dir2;
internal const int NUM_INIT_DOCS = 17;
internal IndexWriter writer2;
internal readonly IList<Exception> failures = new List<Exception>();
internal volatile bool didClose;
internal readonly IndexReader[] readers;
internal readonly int NUM_COPY;
internal const int NUM_THREADS = 5;
internal readonly ThreadJob[] threads = new ThreadJob[NUM_THREADS];
public RunAddIndexesThreads(TestAddIndexes outerInstance, int numCopy)
{
NUM_COPY = numCopy;
dir = new MockDirectoryWrapper(Random, new RAMDirectory());
IndexWriter writer = new IndexWriter(dir, (IndexWriterConfig)new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetMaxBufferedDocs(2));
for (int i = 0; i < NUM_INIT_DOCS; i++)
{
outerInstance.AddDoc(writer);
}
writer.Dispose();
dir2 = NewDirectory();
writer2 = new IndexWriter(dir2, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
writer2.Commit();
readers = new IndexReader[NUM_COPY];
for (int i = 0; i < NUM_COPY; i++)
{
readers[i] = DirectoryReader.Open(dir);
}
}
internal virtual void LaunchThreads(int numIter)
{
for (int i = 0; i < NUM_THREADS; i++)
{
threads[i] = new ThreadAnonymousInnerClassHelper(this, numIter);
}
for (int i = 0; i < NUM_THREADS; i++)
{
threads[i].Start();
}
}
private class ThreadAnonymousInnerClassHelper : ThreadJob
{
private readonly RunAddIndexesThreads outerInstance;
private readonly int numIter;
public ThreadAnonymousInnerClassHelper(RunAddIndexesThreads outerInstance, int numIter)
{
this.outerInstance = outerInstance;
this.numIter = numIter;
}
public override void Run()
{
try
{
Directory[] dirs = new Directory[outerInstance.NUM_COPY];
for (int k = 0; k < outerInstance.NUM_COPY; k++)
{
dirs[k] = new MockDirectoryWrapper(Random, new RAMDirectory(outerInstance.dir, NewIOContext(Random)));
}
int j = 0;
while (true)
{
// System.out.println(Thread.currentThread().getName() + ": iter j=" + j);
if (numIter > 0 && j == numIter)
{
break;
}
outerInstance.DoBody(j++, dirs);
}
}
catch (Exception t)
{
outerInstance.Handle(t);
}
}
}
internal virtual void JoinThreads()
{
for (int i = 0; i < NUM_THREADS; i++)
{
threads[i].Join();
}
}
internal virtual void Close(bool doWait)
{
didClose = true;
writer2.Dispose(doWait);
}
internal virtual void CloseDir()
{
for (int i = 0; i < NUM_COPY; i++)
{
readers[i].Dispose();
}
dir2.Dispose();
}
internal abstract void DoBody(int j, Directory[] dirs);
internal abstract void Handle(Exception t);
}
private class CommitAndAddIndexes : RunAddIndexesThreads
{
public CommitAndAddIndexes(TestAddIndexes outerInstance, int numCopy)
: base(outerInstance, numCopy)
{
}
internal override void Handle(Exception t)
{
Console.Error.WriteLine(t.StackTrace);
lock (failures)
{
failures.Add(t);
}
}
internal override void DoBody(int j, Directory[] dirs)
{
switch (j % 5)
{
case 0:
if (Verbose)
{
Console.WriteLine(Thread.CurrentThread.Name + ": TEST: addIndexes(Dir[]) then full merge");
}
writer2.AddIndexes(dirs);
writer2.ForceMerge(1);
break;
case 1:
if (Verbose)
{
Console.WriteLine(Thread.CurrentThread.Name + ": TEST: addIndexes(Dir[])");
}
writer2.AddIndexes(dirs);
break;
case 2:
if (Verbose)
{
Console.WriteLine(Thread.CurrentThread.Name + ": TEST: addIndexes(IndexReader[])");
}
writer2.AddIndexes(readers);
break;
case 3:
if (Verbose)
{
Console.WriteLine(Thread.CurrentThread.Name + ": TEST: addIndexes(Dir[]) then maybeMerge");
}
writer2.AddIndexes(dirs);
writer2.MaybeMerge();
break;
case 4:
if (Verbose)
{
Console.WriteLine(Thread.CurrentThread.Name + ": TEST: commit");
}
writer2.Commit();
break;
}
}
}
// LUCENE-1335: test simultaneous addIndexes & commits
// from multiple threads
[Test]
[Timeout(300000)]
public virtual void TestAddIndexesWithThreads()
{
int NUM_ITER = TestNightly ? 15 : 5;
const int NUM_COPY = 3;
CommitAndAddIndexes c = new CommitAndAddIndexes(this, NUM_COPY);
c.LaunchThreads(NUM_ITER);
for (int i = 0; i < 100; i++)
{
AddDoc(c.writer2);
}
c.JoinThreads();
int expectedNumDocs = 100 + NUM_COPY * (4 * NUM_ITER / 5) * RunAddIndexesThreads.NUM_THREADS * RunAddIndexesThreads.NUM_INIT_DOCS;
Assert.AreEqual(expectedNumDocs, c.writer2.NumDocs, "expected num docs don't match - failures: " + Environment.NewLine
+ string.Join(Environment.NewLine, c.failures.Select(x => x.ToString())));
c.Close(true);
Assert.IsTrue(c.failures.Count == 0, "found unexpected failures: " + c.failures);
IndexReader reader = DirectoryReader.Open(c.dir2);
Assert.AreEqual(expectedNumDocs, reader.NumDocs);
reader.Dispose();
c.CloseDir();
}
private class CommitAndAddIndexes2 : CommitAndAddIndexes
{
public CommitAndAddIndexes2(TestAddIndexes outerInstance, int numCopy)
: base(outerInstance, numCopy)
{
}
internal override void Handle(Exception t)
{
if (!(t is ObjectDisposedException) && !(t is NullReferenceException))
{
Console.Error.WriteLine(t.StackTrace);
lock (failures)
{
failures.Add(t);
}
}
}
}
// LUCENE-1335: test simultaneous addIndexes & close
[Test]
public virtual void TestAddIndexesWithClose()
{
const int NUM_COPY = 3;
CommitAndAddIndexes2 c = new CommitAndAddIndexes2(this, NUM_COPY);
//c.writer2.setInfoStream(System.out);
c.LaunchThreads(-1);
// Close w/o first stopping/joining the threads
c.Close(true);
//c.writer2.Dispose();
c.JoinThreads();
c.CloseDir();
Assert.IsTrue(c.failures.Count == 0);
}
private class CommitAndAddIndexes3 : RunAddIndexesThreads
{
public CommitAndAddIndexes3(TestAddIndexes outerInstance, int numCopy)
: base(outerInstance, numCopy)
{
}
internal override void DoBody(int j, Directory[] dirs)
{
switch (j % 5)
{
case 0:
if (Verbose)
{
Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": addIndexes + full merge");
}
writer2.AddIndexes(dirs);
writer2.ForceMerge(1);
break;
case 1:
if (Verbose)
{
Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": addIndexes");
}
writer2.AddIndexes(dirs);
break;
case 2:
if (Verbose)
{
Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": addIndexes(IR[])");
}
writer2.AddIndexes(readers);
break;
case 3:
if (Verbose)
{
Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": full merge");
}
writer2.ForceMerge(1);
break;
case 4:
if (Verbose)
{
Console.WriteLine("TEST: " + Thread.CurrentThread.Name + ": commit");
}
writer2.Commit();
break;
}
}
internal override void Handle(Exception t)
{
bool report = true;
if (t is ObjectDisposedException || t is MergePolicy.MergeAbortedException || t is NullReferenceException)
{
report = !didClose;
}
// LUCENENET specific - since NoSuchDirectoryException subclasses FileNotFoundException
// in Lucene, we need to handle it here to be on the safe side.
else if (t is FileNotFoundException/* || t is NoSuchFileException*/ || t is DirectoryNotFoundException)
{
report = !didClose;
}
else if (t is IOException)
{
Exception t2 = t.InnerException;
if (t2 is MergePolicy.MergeAbortedException)
{
report = !didClose;
}
}
if (report)
{
Console.Out.WriteLine(t.StackTrace);
lock (failures)
{
failures.Add(t);
}
}
}
}
// LUCENE-1335: test simultaneous addIndexes & close
[Test]
[Slow]
[Deadlock][Timeout(600000)]
public virtual void TestAddIndexesWithCloseNoWait()
{
const int NUM_COPY = 50;
CommitAndAddIndexes3 c = new CommitAndAddIndexes3(this, NUM_COPY);
c.LaunchThreads(-1);
Thread.Sleep(TestUtil.NextInt32(Random, 10, 500));
// Close w/o first stopping/joining the threads
if (Verbose)
{
Console.WriteLine("TEST: now close(false)");
}
c.Close(false);
c.JoinThreads();
if (Verbose)
{
Console.WriteLine("TEST: done join threads");
}
c.CloseDir();
Assert.IsTrue(c.failures.Count == 0);
}
// LUCENE-1335: test simultaneous addIndexes & close
[Test]
[Timeout(300000)]
public virtual void TestAddIndexesWithRollback()
{
int NUM_COPY = TestNightly ? 50 : 5;
CommitAndAddIndexes3 c = new CommitAndAddIndexes3(this, NUM_COPY);
c.LaunchThreads(-1);
Thread.Sleep(TestUtil.NextInt32(Random, 10, 500));
// Close w/o first stopping/joining the threads
if (Verbose)
{
Console.WriteLine("TEST: now force rollback");
}
c.didClose = true;
c.writer2.Rollback();
c.JoinThreads();
c.CloseDir();
Assert.IsTrue(c.failures.Count == 0);
}
// LUCENE-2996: tests that addIndexes(IndexReader) applies existing deletes correctly.
[Test]
public virtual void TestExistingDeletes()
{
Directory[] dirs = new Directory[2];
for (int i = 0; i < dirs.Length; i++)
{
dirs[i] = NewDirectory();
IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
IndexWriter writer = new IndexWriter(dirs[i], conf);
Document doc = new Document();
doc.Add(new StringField("id", "myid", Field.Store.NO));
writer.AddDocument(doc);
writer.Dispose();
}
IndexWriterConfig conf_ = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
IndexWriter writer_ = new IndexWriter(dirs[0], conf_);
// Now delete the document
writer_.DeleteDocuments(new Term("id", "myid"));
IndexReader r = DirectoryReader.Open(dirs[1]);
try
{
writer_.AddIndexes(r);
}
finally
{
r.Dispose();
}
writer_.Commit();
Assert.AreEqual(1, writer_.NumDocs, "Documents from the incoming index should not have been deleted");
writer_.Dispose();
foreach (Directory dir in dirs)
{
dir.Dispose();
}
}
// just like addDocs but with ID, starting from docStart
private void AddDocsWithID(IndexWriter writer, int numDocs, int docStart)
{
for (int i = 0; i < numDocs; i++)
{
Document doc = new Document();
doc.Add(NewTextField("content", "aaa", Field.Store.NO));
doc.Add(NewTextField("id", "" + (docStart + i), Field.Store.YES));
writer.AddDocument(doc);
}
}
[Test]
public virtual void TestSimpleCaseCustomCodec()
{
// main directory
Directory dir = NewDirectory();
// two auxiliary directories
Directory aux = NewDirectory();
Directory aux2 = NewDirectory();
Codec codec = new CustomPerFieldCodec();
IndexWriter writer = null;
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetCodec(codec));
// add 100 documents
AddDocsWithID(writer, 100, 0);
Assert.AreEqual(100, writer.MaxDoc);
writer.Commit();
writer.Dispose();
TestUtil.CheckIndex(dir);
writer = NewWriter(aux, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetCodec(codec).SetMaxBufferedDocs(10).SetMergePolicy(NewLogMergePolicy(false)));
// add 40 documents in separate files
AddDocs(writer, 40);
Assert.AreEqual(40, writer.MaxDoc);
writer.Commit();
writer.Dispose();
writer = NewWriter(aux2, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.CREATE).SetCodec(codec));
// add 40 documents in compound files
AddDocs2(writer, 50);
Assert.AreEqual(50, writer.MaxDoc);
writer.Commit();
writer.Dispose();
// test doc count before segments are merged
writer = NewWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)).SetOpenMode(OpenMode.APPEND).SetCodec(codec));
Assert.AreEqual(100, writer.MaxDoc);
writer.AddIndexes(aux, aux2);
Assert.AreEqual(190, writer.MaxDoc);
writer.Dispose();
dir.Dispose();
aux.Dispose();
aux2.Dispose();
}
private sealed class CustomPerFieldCodec : Lucene46Codec
{
internal readonly PostingsFormat simpleTextFormat;
internal readonly PostingsFormat defaultFormat;
internal readonly PostingsFormat mockSepFormat;
public CustomPerFieldCodec()
{
simpleTextFormat = Codecs.PostingsFormat.ForName("SimpleText");
defaultFormat = Codecs.PostingsFormat.ForName("Lucene41");
mockSepFormat = Codecs.PostingsFormat.ForName("MockSep");
}
public override PostingsFormat GetPostingsFormatForField(string field)
{
if (field.Equals("id", StringComparison.Ordinal))
{
return simpleTextFormat;
}
else if (field.Equals("content", StringComparison.Ordinal))
{
return mockSepFormat;
}
else
{
return defaultFormat;
}
}
}
// LUCENE-2790: tests that the non CFS files were deleted by addIndexes
[Test]
public virtual void TestNonCFSLeftovers()
{
Directory[] dirs = new Directory[2];
for (int i = 0; i < dirs.Length; i++)
{
dirs[i] = new RAMDirectory();
IndexWriter w = new IndexWriter(dirs[i], new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
Document d = new Document();
FieldType customType = new FieldType(TextField.TYPE_STORED);
customType.StoreTermVectors = true;
d.Add(new Field("c", "v", customType));
w.AddDocument(d);
w.Dispose();
}
IndexReader[] readers = new IndexReader[] { DirectoryReader.Open(dirs[0]), DirectoryReader.Open(dirs[1]) };
Directory dir = new MockDirectoryWrapper(Random, new RAMDirectory());
IndexWriterConfig conf = (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetMergePolicy(NewLogMergePolicy(true));
MergePolicy lmp = conf.MergePolicy;
// Force creation of CFS:
lmp.NoCFSRatio = 1.0;
lmp.MaxCFSSegmentSizeMB = double.PositiveInfinity;
IndexWriter w3 = new IndexWriter(dir, conf);
w3.AddIndexes(readers);
w3.Dispose();
// we should now see segments_X,
// segments.gen,_Y.cfs,_Y.cfe, _Z.si
Assert.AreEqual(5, dir.ListAll().Length, "Only one compound segment should exist, but got: " + Arrays.ToString(dir.ListAll()));
dir.Dispose();
}
[CodecName("NotRegistered")]
private sealed class UnRegisteredCodec : FilterCodec
{
public UnRegisteredCodec()
: base(new Lucene46Codec())
{
}
}
/*
* simple test that ensures we getting expected exceptions
*/
[Test]
public virtual void TestAddIndexMissingCodec()
{
BaseDirectoryWrapper toAdd = NewDirectory();
// Disable checkIndex, else we get an exception because
// of the unregistered codec:
toAdd.CheckIndexOnDispose = false;
{
IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
conf.SetCodec(new UnRegisteredCodec());
using var w = new IndexWriter(toAdd, conf);
Document doc = new Document();
FieldType customType = new FieldType();
customType.IsIndexed = true;
doc.Add(NewField("foo", "bar", customType));
w.AddDocument(doc);
}
{
using Directory dir = NewDirectory();
IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
conf.SetCodec(TestUtil.AlwaysPostingsFormat(new Pulsing41PostingsFormat(1 + Random.Next(20))));
IndexWriter w = new IndexWriter(dir, conf);
try
{
w.AddIndexes(toAdd);
Assert.Fail("no such codec");
}
#pragma warning disable 168
catch (ArgumentException ex)
#pragma warning restore 168
{
// expected
}
finally
{
w.Dispose();
}
using IndexReader open = DirectoryReader.Open(dir);
Assert.AreEqual(0, open.NumDocs);
}
try
{
DirectoryReader.Open(toAdd);
Assert.Fail("no such codec");
}
#pragma warning disable 168
catch (ArgumentException ex)
#pragma warning restore 168
{
// expected
}
toAdd.Dispose();
}
// LUCENE-3575
[Test]
public virtual void TestFieldNamesChanged()
{
Directory d1 = NewDirectory();
RandomIndexWriter w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, d1);
Document doc = new Document();
doc.Add(NewStringField("f1", "doc1 field1", Field.Store.YES));
doc.Add(NewStringField("id", "1", Field.Store.YES));
w.AddDocument(doc);
IndexReader r1 = w.GetReader();
w.Dispose();
Directory d2 = NewDirectory();
w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, d2);
doc = new Document();
doc.Add(NewStringField("f2", "doc2 field2", Field.Store.YES));
doc.Add(NewStringField("id", "2", Field.Store.YES));
w.AddDocument(doc);
IndexReader r2 = w.GetReader();
w.Dispose();
Directory d3 = NewDirectory();
w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, d3);
w.AddIndexes(r1, r2);
r1.Dispose();
d1.Dispose();
r2.Dispose();
d2.Dispose();
IndexReader r3 = w.GetReader();
w.Dispose();
Assert.AreEqual(2, r3.NumDocs);
for (int docID = 0; docID < 2; docID++)
{
Document d = r3.Document(docID);
if (d.Get("id").Equals("1", StringComparison.Ordinal))
{
Assert.AreEqual("doc1 field1", d.Get("f1"));
}
else
{
Assert.AreEqual("doc2 field2", d.Get("f2"));
}
}
r3.Dispose();
d3.Dispose();
}
[Test]
public virtual void TestAddEmpty()
{
Directory d1 = NewDirectory();
RandomIndexWriter w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, d1);
MultiReader empty = new MultiReader();
w.AddIndexes(empty);
w.Dispose();
DirectoryReader dr = DirectoryReader.Open(d1);
foreach (AtomicReaderContext ctx in dr.Leaves)
{
Assert.IsTrue(ctx.Reader.MaxDoc > 0, "empty segments should be dropped by addIndexes");
}
dr.Dispose();
d1.Dispose();
}
// Currently it's impossible to end up with a segment with all documents
// deleted, as such segments are dropped. Still, to validate that addIndexes
// works with such segments, or readers that end up in such state, we fake an
// all deleted segment.
[Test]
public virtual void TestFakeAllDeleted()
{
Directory src = NewDirectory(), dest = NewDirectory();
RandomIndexWriter w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, src);
w.AddDocument(new Document());
IndexReader allDeletedReader = new AllDeletedFilterReader((AtomicReader)w.GetReader().Leaves[0].Reader);
w.Dispose();
w = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, dest);
w.AddIndexes(allDeletedReader);
w.Dispose();
DirectoryReader dr = DirectoryReader.Open(src);
foreach (AtomicReaderContext ctx in dr.Leaves)
{
Assert.IsTrue(ctx.Reader.MaxDoc > 0, "empty segments should be dropped by addIndexes");
}
dr.Dispose();
allDeletedReader.Dispose();
src.Dispose();
dest.Dispose();
}
/// <summary>
/// Make sure an open IndexWriter on an incoming Directory
/// causes a LockObtainFailedException
/// </summary>
[Test]
public virtual void TestLocksBlock()
{
Directory src = NewDirectory();
RandomIndexWriter w1 = new RandomIndexWriter(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
this,
#endif
Random, src);
w1.AddDocument(new Document());
w1.Commit();
Directory dest = NewDirectory();
IndexWriterConfig iwc = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
iwc.SetWriteLockTimeout(1);
RandomIndexWriter w2 = new RandomIndexWriter(Random, dest, iwc);
try
{
w2.AddIndexes(src);
Assert.Fail("did not hit expected exception");
}
#pragma warning disable 168
catch (LockObtainFailedException lofe)
#pragma warning restore 168
{
// expected
}
IOUtils.Dispose(w1, w2, src, dest);
}
}
}