blob: f1eae0b737265390121818a6e8ac1d9bac2ae831 [file] [log] [blame]
using J2N.Threading;
using Lucene.Net.Documents;
using Lucene.Net.Index.Extensions;
using Lucene.Net.Store;
using Lucene.Net.Util;
using NUnit.Framework;
using System;
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 MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
[TestFixture]
public class TestAtomicUpdate : LuceneTestCase
{
private abstract class TimedThread : ThreadJob
{
internal volatile bool failed;
internal int count;
internal static float RUN_TIME_MSEC = AtLeast(500);
internal TimedThread[] allThreads;
public abstract void DoWork();
internal TimedThread(TimedThread[] threads)
{
this.allThreads = threads;
}
public override void Run()
{
long stopTime = Environment.TickCount + (long)RUN_TIME_MSEC;
count = 0;
try
{
do
{
if (AnyErrors())
{
break;
}
DoWork();
count++;
} while (Environment.TickCount < stopTime);
}
catch (Exception e)
{
Console.WriteLine(Thread.CurrentThread.Name + ": exc");
Console.WriteLine(e.StackTrace);
failed = true;
}
}
internal virtual bool AnyErrors()
{
for (int i = 0; i < allThreads.Length; i++)
{
if (allThreads[i] != null && allThreads[i].failed)
{
return true;
}
}
return false;
}
}
private class IndexerThread : TimedThread
{
internal IndexWriter writer;
public IndexerThread(IndexWriter writer, TimedThread[] threads)
: base(threads)
{
this.writer = writer;
}
public override void DoWork()
{
// Update all 100 docs...
for (int i = 0; i < 100; i++)
{
Documents.Document d = new Documents.Document();
d.Add(new StringField("id", Convert.ToString(i), Field.Store.YES));
d.Add(new TextField("contents", English.Int32ToEnglish(i + 10 * count), Field.Store.NO));
writer.UpdateDocument(new Term("id", Convert.ToString(i)), d);
}
}
}
private class SearcherThread : TimedThread
{
internal Directory directory;
public SearcherThread(Directory directory, TimedThread[] threads)
: base(threads)
{
this.directory = directory;
}
public override void DoWork()
{
IndexReader r = DirectoryReader.Open(directory);
Assert.AreEqual(100, r.NumDocs);
r.Dispose();
}
}
/*
Run one indexer and 2 searchers against single index as
stress test.
*/
public virtual void RunTest(Directory directory)
{
TimedThread[] threads = new TimedThread[4];
IndexWriterConfig conf = (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetMaxBufferedDocs(7);
((TieredMergePolicy)conf.MergePolicy).MaxMergeAtOnce = 3;
IndexWriter writer = RandomIndexWriter.MockIndexWriter(directory, conf, Random);
// Establish a base index of 100 docs:
for (int i = 0; i < 100; i++)
{
Documents.Document d = new Documents.Document();
d.Add(NewStringField("id", Convert.ToString(i), Field.Store.YES));
d.Add(NewTextField("contents", English.Int32ToEnglish(i), Field.Store.NO));
if ((i - 1) % 7 == 0)
{
writer.Commit();
}
writer.AddDocument(d);
}
writer.Commit();
IndexReader r = DirectoryReader.Open(directory);
Assert.AreEqual(100, r.NumDocs);
r.Dispose();
IndexerThread indexerThread = new IndexerThread(writer, threads);
threads[0] = indexerThread;
indexerThread.Start();
IndexerThread indexerThread2 = new IndexerThread(writer, threads);
threads[1] = indexerThread2;
indexerThread2.Start();
SearcherThread searcherThread1 = new SearcherThread(directory, threads);
threads[2] = searcherThread1;
searcherThread1.Start();
SearcherThread searcherThread2 = new SearcherThread(directory, threads);
threads[3] = searcherThread2;
searcherThread2.Start();
indexerThread.Join();
indexerThread2.Join();
searcherThread1.Join();
searcherThread2.Join();
writer.Dispose();
Assert.IsTrue(!indexerThread.failed, "hit unexpected exception in indexer");
Assert.IsTrue(!indexerThread2.failed, "hit unexpected exception in indexer2");
Assert.IsTrue(!searcherThread1.failed, "hit unexpected exception in search1");
Assert.IsTrue(!searcherThread2.failed, "hit unexpected exception in search2");
//System.out.println(" Writer: " + indexerThread.count + " iterations");
//System.out.println("Searcher 1: " + searcherThread1.count + " searchers created");
//System.out.println("Searcher 2: " + searcherThread2.count + " searchers created");
}
/*
Run above stress test against RAMDirectory and then
FSDirectory.
*/
[Test]
[Slow]
public virtual void TestAtomicUpdates()
{
Directory directory;
// First in a RAM directory:
using (directory = new MockDirectoryWrapper(Random, new RAMDirectory()))
{
RunTest(directory);
}
// Second in an FSDirectory:
System.IO.DirectoryInfo dirPath = CreateTempDir("lucene.test.atomic");
using (directory = NewFSDirectory(dirPath))
{
RunTest(directory);
}
System.IO.Directory.Delete(dirPath.FullName, true);
}
}
}