blob: b8018b6213efb8a129e452141969d7233a5ce496 [file] [log] [blame]
using J2N.Threading;
using Lucene.Net.Documents;
using Lucene.Net.Index.Extensions;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Assert = Lucene.Net.TestFramework.Assert;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Store
{
/*
* 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 DirectoryReader = Lucene.Net.Index.DirectoryReader;
using Document = Documents.Document;
using Field = Field;
using IndexReader = Lucene.Net.Index.IndexReader;
using IndexSearcher = Lucene.Net.Search.IndexSearcher;
using IndexWriter = Lucene.Net.Index.IndexWriter;
using IndexWriterConfig = Lucene.Net.Index.IndexWriterConfig;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
using OpenMode = Lucene.Net.Index.OpenMode;
using Query = Lucene.Net.Search.Query;
using Term = Lucene.Net.Index.Term;
using TermQuery = Lucene.Net.Search.TermQuery;
[TestFixture]
public class TestLockFactory : LuceneTestCase
{
// Verify: we can provide our own LockFactory implementation, the right
// methods are called at the right time, locks are created, etc.
[Test]
public virtual void TestCustomLockFactory()
{
Directory dir = new MockDirectoryWrapper(Random, new RAMDirectory());
MockLockFactory lf = new MockLockFactory(this);
dir.SetLockFactory(lf);
// Lock prefix should have been set:
Assert.IsTrue(lf.LockPrefixSet, "lock prefix was not set by the RAMDirectory");
IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
// add 100 documents (so that commit lock is used)
for (int i = 0; i < 100; i++)
{
AddDoc(writer);
}
// Both write lock and commit lock should have been created:
Assert.AreEqual(1, lf.LocksCreated.Count, "# of unique locks created (after instantiating IndexWriter)");
Assert.IsTrue(lf.MakeLockCount >= 1, "# calls to makeLock is 0 (after instantiating IndexWriter)");
foreach (String lockName in lf.LocksCreated.Keys)
{
MockLockFactory.MockLock @lock = (MockLockFactory.MockLock)lf.LocksCreated[lockName];
Assert.IsTrue(@lock.LockAttempts > 0, "# calls to Lock.obtain is 0 (after instantiating IndexWriter)");
}
writer.Dispose();
}
// Verify: we can use the NoLockFactory with RAMDirectory w/ no
// exceptions raised:
// Verify: NoLockFactory allows two IndexWriters
[Test]
public virtual void TestRAMDirectoryNoLocking()
{
MockDirectoryWrapper dir = new MockDirectoryWrapper(Random, new RAMDirectory());
dir.SetLockFactory(NoLockFactory.GetNoLockFactory());
dir.WrapLockFactory = false; // we are gonna explicitly test we get this back
Assert.IsTrue(typeof(NoLockFactory).IsInstanceOfType(dir.LockFactory), "RAMDirectory.setLockFactory did not take");
IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
writer.Commit(); // required so the second open succeed
// Create a 2nd IndexWriter. this is normally not allowed but it should run through since we're not
// using any locks:
IndexWriter writer2 = null;
try
{
writer2 = new IndexWriter(dir, (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetOpenMode(OpenMode.APPEND));
}
catch (Exception e)
{
Console.Out.Write(e.StackTrace);
Assert.Fail("Should not have hit an IOException with no locking");
}
writer.Dispose();
if (writer2 != null)
{
writer2.Dispose();
}
}
// Verify: SingleInstanceLockFactory is the default lock for RAMDirectory
// Verify: RAMDirectory does basic locking correctly (can't create two IndexWriters)
[Test]
public virtual void TestDefaultRAMDirectory()
{
Directory dir = new RAMDirectory();
Assert.IsTrue(typeof(SingleInstanceLockFactory).IsInstanceOfType(dir.LockFactory), "RAMDirectory did not use correct LockFactory: got " + dir.LockFactory);
IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
// Create a 2nd IndexWriter. this should fail:
IndexWriter writer2 = null;
try
{
writer2 = new IndexWriter(dir, (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetOpenMode(OpenMode.APPEND));
Assert.Fail("Should have hit an IOException with two IndexWriters on default SingleInstanceLockFactory");
}
#pragma warning disable 168
catch (IOException e)
#pragma warning restore 168
{
}
writer.Dispose();
if (writer2 != null)
{
writer2.Dispose();
}
}
[Test]
public virtual void TestSimpleFSLockFactory()
{
// test string file instantiation
new SimpleFSLockFactory("test");
}
// Verify: do stress test, by opening IndexReaders and
// IndexWriters over & over in 2 threads and making sure
// no unexpected exceptions are raised:
[Test]
[Nightly]
public virtual void TestStressLocks()
{
_testStressLocks(null, CreateTempDir("index.TestLockFactory6"));
}
// Verify: do stress test, by opening IndexReaders and
// IndexWriters over & over in 2 threads and making sure
// no unexpected exceptions are raised, but use
// NativeFSLockFactory:
[Test]
[Nightly]
public virtual void TestStressLocksNativeFSLockFactory()
{
DirectoryInfo dir = CreateTempDir("index.TestLockFactory7");
_testStressLocks(new NativeFSLockFactory(dir), dir);
}
public virtual void _testStressLocks(LockFactory lockFactory, DirectoryInfo indexDir)
{
Directory dir = NewFSDirectory(indexDir, lockFactory);
// First create a 1 doc index:
IndexWriter w = new IndexWriter(dir, (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetOpenMode(OpenMode.CREATE));
AddDoc(w);
w.Dispose();
WriterThread writer = new WriterThread(this, 100, dir);
SearcherThread searcher = new SearcherThread(this, 100, dir);
writer.Start();
searcher.Start();
while (writer.IsAlive || searcher.IsAlive)
{
Thread.Sleep(1000);
}
Assert.IsTrue(!writer.HitException, "IndexWriter hit unexpected exceptions");
Assert.IsTrue(!searcher.HitException, "IndexSearcher hit unexpected exceptions");
dir.Dispose();
// Cleanup
System.IO.Directory.Delete(indexDir.FullName, true);
}
// Verify: NativeFSLockFactory works correctly
[Test]
public virtual void TestNativeFSLockFactory()
{
var f = new NativeFSLockFactory(CreateTempDir("testNativeFsLockFactory"));
f.LockPrefix = "test";
var l = f.MakeLock("commit");
var l2 = f.MakeLock("commit");
Assert.IsTrue(l.Obtain(), "failed to obtain lock, got exception: {0}", l.FailureReason);
Assert.IsTrue(!l2.Obtain(), "succeeded in obtaining lock twice");
l.Dispose();
Assert.IsTrue(l2.Obtain(), "failed to obtain 2nd lock after first one was freed, got exception: {0}", l2.FailureReason);
l2.Dispose();
// Make sure we can obtain first one again, test isLocked():
Assert.IsTrue(l.Obtain(), "failed to obtain lock, got exception: {0}", l.FailureReason);
Assert.IsTrue(l.IsLocked());
Assert.IsTrue(l2.IsLocked());
l.Dispose();
Assert.IsFalse(l.IsLocked());
Assert.IsFalse(l2.IsLocked());
}
// Verify: NativeFSLockFactory works correctly if the lock file exists
[Test]
public virtual void TestNativeFSLockFactoryLockExists()
{
DirectoryInfo tempDir = CreateTempDir("testNativeFsLockFactory");
// Touch the lock file
var lockFile = new FileInfo(Path.Combine(tempDir.FullName, "test.lock"));
using (lockFile.Create()){};
var l = (new NativeFSLockFactory(tempDir)).MakeLock("test.lock");
Assert.IsTrue(l.Obtain(), "failed to obtain lock, got exception: {0}", l.FailureReason);
l.Dispose();
Assert.IsFalse(l.IsLocked(), "failed to release lock, got exception: {0}", l.FailureReason);
if (lockFile.Exists)
{
lockFile.Delete();
}
}
// Verify: NativeFSLockFactory assigns null as lockPrefix if the lockDir is inside directory
[Test]
public virtual void TestNativeFSLockFactoryPrefix()
{
DirectoryInfo fdir1 = CreateTempDir("TestLockFactory.8");
DirectoryInfo fdir2 = CreateTempDir("TestLockFactory.8.Lockdir");
Directory dir1 = NewFSDirectory(fdir1, new NativeFSLockFactory(fdir1));
// same directory, but locks are stored somewhere else. The prefix of the lock factory should != null
Directory dir2 = NewFSDirectory(fdir1, new NativeFSLockFactory(fdir2));
string prefix1 = dir1.LockFactory.LockPrefix;
Assert.IsNull(prefix1, "Lock prefix for lockDir same as directory should be null");
string prefix2 = dir2.LockFactory.LockPrefix;
Assert.IsNotNull(prefix2, "Lock prefix for lockDir outside of directory should be not null");
dir1.Dispose();
dir2.Dispose();
System.IO.Directory.Delete(fdir1.FullName, true);
System.IO.Directory.Delete(fdir2.FullName, true);
}
// Verify: default LockFactory has no prefix (ie
// write.lock is stored in index):
[Test]
public virtual void TestDefaultFSLockFactoryPrefix()
{
// Make sure we get null prefix, which wont happen if setLockFactory is ever called.
DirectoryInfo dirName = CreateTempDir("TestLockFactory.10");
Directory dir = new SimpleFSDirectory(dirName);
Assert.IsNull(dir.LockFactory.LockPrefix, "Default lock prefix should be null");
dir.Dispose();
dir = new MMapDirectory(dirName);
Assert.IsNull(dir.LockFactory.LockPrefix, "Default lock prefix should be null");
dir.Dispose();
dir = new NIOFSDirectory(dirName);
Assert.IsNull(dir.LockFactory.LockPrefix, "Default lock prefix should be null");
dir.Dispose();
System.IO.Directory.Delete(dirName.FullName, true);
}
private class WriterThread : ThreadJob
{
private readonly TestLockFactory outerInstance;
private readonly Directory dir;
private readonly int numIteration;
public bool HitException { get; private set; } = false;
public WriterThread(TestLockFactory outerInstance, int numIteration, Directory dir)
{
this.outerInstance = outerInstance;
this.numIteration = numIteration;
this.dir = dir;
}
public override void Run()
{
IndexWriter writer = null;
for (int i = 0; i < this.numIteration; i++)
{
try
{
writer = new IndexWriter(dir, (new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random))).SetOpenMode(OpenMode.APPEND));
}
catch (IOException e)
{
if (e.ToString().IndexOf(" timed out:", StringComparison.Ordinal) == -1)
{
HitException = true;
Console.WriteLine("Stress Test Index Writer: creation hit unexpected IOException: " + e.ToString());
Console.Out.Write(e.StackTrace);
}
else
{
// lock obtain timed out
// NOTE: we should at some point
// consider this a failure? The lock
// obtains, across IndexReader &
// IndexWriters should be "fair" (ie
// FIFO).
}
}
catch (Exception e)
{
HitException = true;
Console.WriteLine("Stress Test Index Writer: creation hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
if (writer != null)
{
try
{
outerInstance.AddDoc(writer);
}
catch (IOException e)
{
HitException = true;
Console.WriteLine("Stress Test Index Writer: addDoc hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
try
{
writer.Dispose();
}
catch (IOException e)
{
HitException = true;
Console.WriteLine("Stress Test Index Writer: close hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
writer = null;
}
}
}
}
private class SearcherThread : ThreadJob
{
private readonly TestLockFactory outerInstance;
private readonly Directory dir;
private readonly int numIteration;
public bool HitException { get; private set; } = false;
public SearcherThread(TestLockFactory outerInstance, int numIteration, Directory dir)
{
this.outerInstance = outerInstance;
this.numIteration = numIteration;
this.dir = dir;
}
public override void Run()
{
IndexReader reader = null;
IndexSearcher searcher = null;
Query query = new TermQuery(new Term("content", "aaa"));
for (int i = 0; i < this.numIteration; i++)
{
try
{
reader = DirectoryReader.Open(dir);
searcher = NewSearcher(
#if FEATURE_INSTANCE_TESTDATA_INITIALIZATION
OuterInstance,
#endif
reader);
}
catch (Exception e)
{
HitException = true;
Console.WriteLine("Stress Test Index Searcher: create hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
try
{
searcher.Search(query, null, 1000);
}
catch (IOException e)
{
HitException = true;
Console.WriteLine("Stress Test Index Searcher: search hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
// System.out.println(hits.Length() + " total results");
try
{
reader.Dispose();
}
catch (IOException e)
{
HitException = true;
Console.WriteLine("Stress Test Index Searcher: close hit unexpected exception: " + e.ToString());
Console.Out.Write(e.StackTrace);
break;
}
}
}
}
public class MockLockFactory : LockFactory
{
private readonly TestLockFactory outerInstance;
public MockLockFactory(TestLockFactory outerInstance)
{
this.outerInstance = outerInstance;
}
public bool LockPrefixSet;
public IDictionary<string, Lock> LocksCreated = /*CollectionsHelper.SynchronizedMap(*/new Dictionary<string, Lock>()/*)*/;
public int MakeLockCount = 0;
public override string LockPrefix
{
set
{
base.LockPrefix = value;
LockPrefixSet = true;
}
}
public override Lock MakeLock(string lockName)
{
lock (this)
{
Lock @lock = new MockLock(this);
LocksCreated[lockName] = @lock;
MakeLockCount++;
return @lock;
}
}
public override void ClearLock(string specificLockName)
{
}
public class MockLock : Lock
{
private readonly TestLockFactory.MockLockFactory outerInstance;
public MockLock(TestLockFactory.MockLockFactory outerInstance)
{
this.outerInstance = outerInstance;
}
public int LockAttempts;
public override bool Obtain()
{
LockAttempts++;
return true;
}
protected override void Dispose(bool disposing)
{
// do nothing
}
public override bool IsLocked()
{
return false;
}
}
}
private void AddDoc(IndexWriter writer)
{
Document doc = new Document();
doc.Add(NewTextField("content", "aaa", Field.Store.NO));
writer.AddDocument(doc);
}
}
}