/* * 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 Document = Lucene.Net.Documents.Document; using Field = Lucene.Net.Documents.Field; using IndexReader = Lucene.Net.Index.IndexReader; using IndexWriter = Lucene.Net.Index.IndexWriter; using Term = Lucene.Net.Index.Term; using WhitespaceAnalyzer = Lucene.Net.Analysis.WhitespaceAnalyzer; using Hits = Lucene.Net.Search.Hits; using IndexSearcher = Lucene.Net.Search.IndexSearcher; using Query = Lucene.Net.Search.Query; using TermQuery = Lucene.Net.Search.TermQuery; using LuceneTestCase = Lucene.Net.Util.LuceneTestCase; namespace Lucene.Net.Store { [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 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 WhitespaceAnalyzer(), true); // 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)"); for (System.Collections.IEnumerator e = lf.locksCreated.Keys.GetEnumerator(); e.MoveNext(); ) { System.String lockName = (System.String) e.Current; MockLockFactory.MockLock lock_Renamed = (MockLockFactory.MockLock) lf.locksCreated[lockName]; Assert.IsTrue(lock_Renamed.lockAttempts > 0, "# calls to Lock.obtain is 0 (after instantiating IndexWriter)"); } writer.Close(); } // Verify: we can use the NoLockFactory with RAMDirectory w/ no // exceptions raised: // Verify: NoLockFactory allows two IndexWriters [Test] public virtual void TestRAMDirectoryNoLocking() { Directory dir = new RAMDirectory(); dir.SetLockFactory(NoLockFactory.GetNoLockFactory()); Assert.IsTrue(typeof(NoLockFactory).IsInstanceOfType(dir.GetLockFactory()), "RAMDirectory.setLockFactory did not take"); IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); // 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 WhitespaceAnalyzer(), false); } catch (System.Exception e) { System.Console.Out.WriteLine(e.StackTrace); Assert.Fail("Should not have hit an IOException with no locking"); } writer.Close(); if (writer2 != null) { writer2.Close(); } } // 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.GetLockFactory()), "RAMDirectory did not use correct LockFactory: got " + dir.GetLockFactory()); IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true); // Create a 2nd IndexWriter. This should fail: IndexWriter writer2 = null; try { writer2 = new IndexWriter(dir, new WhitespaceAnalyzer(), false); Assert.Fail("Should have hit an IOException with two IndexWriters on default SingleInstanceLockFactory"); } catch (System.IO.IOException) { } writer.Close(); if (writer2 != null) { writer2.Close(); } } // Verify: SimpleFSLockFactory is the default for FSDirectory // Verify: FSDirectory does basic locking correctly [Test] public virtual void TestDefaultFSDirectory() { System.String indexDirName = "index.TestLockFactory1"; IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(SimpleFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()) || typeof(NativeFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); IndexWriter writer2 = null; // Create a 2nd IndexWriter. This should fail: try { writer2 = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), false); Assert.Fail("Should have hit an IOException with two IndexWriters on default SimpleFSLockFactory"); } catch (System.IO.IOException) { } writer.Close(); if (writer2 != null) { writer2.Close(); } // Cleanup RmDir(indexDirName); } // Verify: FSDirectory's default lockFactory clears all locks correctly [Test] public virtual void TestFSDirectoryTwoCreates() { System.String indexDirName = "index.TestLockFactory2"; IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(SimpleFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()) || typeof(NativeFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); // Intentionally do not close the first writer here. // The goal is to "simulate" a crashed writer and // ensure the second writer, with create=true, is // able to remove the lock files. This works OK // with SimpleFSLockFactory as the locking // implementation. Note, however, that this test // will not work on WIN32 when we switch to // NativeFSLockFactory as the default locking for // FSDirectory because the second IndexWriter cannot // remove those lock files since they are held open // by the first writer. This is because leaving the // first IndexWriter open is not really a good way // to simulate a crashed writer. // Create a 2nd IndexWriter. This should not fail: IndexWriter writer2 = null; try { writer2 = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); } catch (System.IO.IOException e) { System.Console.Error.WriteLine(e.StackTrace); Assert.Fail("Should not have hit an IOException with two IndexWriters with create=true, on default SimpleFSLockFactory"); } writer.Close(); if (writer2 != null) { try { writer2.Close(); // expected } catch (LockReleaseFailedException) { Assert.Fail("writer2.close() should not have hit LockReleaseFailedException"); } } // Cleanup RmDir(indexDirName); } // Verify: setting custom lock factory class (as system property) works: // Verify: all 4 builtin LockFactory implementations are // settable this way // Verify: FSDirectory does basic locking correctly [Test] public virtual void TestLockClassProperty() { System.String indexDirName = "index.TestLockFactory3"; System.String prpName = "Lucene.Net.Store.FSDirectoryLockFactoryClass"; try { // NoLockFactory: SupportClass.AppSettings.Set(prpName, "Lucene.Net.Store.NoLockFactory"); IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(NoLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); writer.Close(); // SingleInstanceLockFactory: SupportClass.AppSettings.Set(prpName, "Lucene.Net.Store.SingleInstanceLockFactory"); writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(SingleInstanceLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); writer.Close(); // NativeFSLockFactory: SupportClass.AppSettings.Set(prpName, "Lucene.Net.Store.NativeFSLockFactory"); writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(NativeFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); writer.Close(); // SimpleFSLockFactory: SupportClass.AppSettings.Set(prpName, "Lucene.Net.Store.SimpleFSLockFactory"); writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(SimpleFSLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct LockFactory: got " + writer.GetDirectory().GetLockFactory()); writer.Close(); } finally { // Put back to the correct default for subsequent tests: SupportClass.AppSettings.Set("Lucene.Net.Store.FSDirectoryLockFactoryClass", ""); } // Cleanup RmDir(indexDirName); } // Verify: setDisableLocks works [Test] public virtual void TestDisableLocks() { System.String indexDirName = "index.TestLockFactory4"; Assert.IsTrue(!FSDirectory.GetDisableLocks(), "Locks are already disabled"); FSDirectory.SetDisableLocks(true); IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true); Assert.IsTrue(typeof(NoLockFactory).IsInstanceOfType(writer.GetDirectory().GetLockFactory()), "FSDirectory did not use correct default LockFactory: got " + writer.GetDirectory().GetLockFactory()); // Should be no error since locking is disabled: IndexWriter writer2 = null; try { writer2 = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), false); } catch (System.IO.IOException e) { System.Console.Error.WriteLine(e.StackTrace); Assert.Fail("Should not have hit an IOException with locking disabled"); } FSDirectory.SetDisableLocks(false); writer.Close(); if (writer2 != null) { writer2.Close(); } // Cleanup RmDir(indexDirName); } // Verify: if I try to getDirectory() with two different locking implementations, I get an IOException [Test] public virtual void TestFSDirectoryDifferentLockFactory() { System.String indexDirName = "index.TestLockFactory5"; LockFactory lf = new SingleInstanceLockFactory(); FSDirectory fs1 = FSDirectory.GetDirectory(indexDirName, lf); // Different lock factory instance should hit IOException: try { FSDirectory fs2 = FSDirectory.GetDirectory(indexDirName, new SingleInstanceLockFactory()); Assert.Fail("Should have hit an IOException because LockFactory instances differ"); } catch (System.IO.IOException) { } FSDirectory fs3 = null; // Same lock factory instance should not: try { fs3 = FSDirectory.GetDirectory(indexDirName, lf); } catch (System.IO.IOException e) { System.Console.Error.WriteLine(e.StackTrace); Assert.Fail("Should not have hit an IOException because LockFactory instances are the same"); } fs1.Close(); if (fs3 != null) { fs3.Close(); } // Cleanup RmDir(indexDirName); } // Verify: do stress test, by opening IndexReaders and // IndexWriters over & over in 2 threads and making sure // no unexpected exceptions are raised: [Test] public virtual void TestStressLocks() { _TestStressLocks(null, "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: public virtual void testStressLocksNativeFSLockFactory() { _TestStressLocks(new NativeFSLockFactory("index.TestLockFactory7"), "index.TestLockFactory7"); } public virtual void _TestStressLocks(LockFactory lockFactory, System.String indexDirName) { FSDirectory fs1 = FSDirectory.GetDirectory(indexDirName, lockFactory); // First create a 1 doc index: IndexWriter w = new IndexWriter(fs1, new WhitespaceAnalyzer(), true); AddDoc(w); w.Close(); WriterThread writer = new WriterThread(this, 100, fs1); SearcherThread searcher = new SearcherThread(this, 100, fs1); writer.Start(); searcher.Start(); while (writer.IsAlive || searcher.IsAlive) { try { System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * 1000)); } catch (System.Threading.ThreadInterruptedException) { } } Assert.IsTrue(!writer.hitException, "IndexWriter hit unexpected exceptions"); Assert.IsTrue(!searcher.hitException, "IndexSearcher hit unexpected exceptions"); // Cleanup RmDir(indexDirName); } // Verify: NativeFSLockFactory works correctly [Test] public virtual void TestNativeFSLockFactory() { System.String altTempDir = System.IO.Path.GetTempPath(); NativeFSLockFactory f = new NativeFSLockFactory(SupportClass.AppSettings.Get("tempDir", altTempDir)); NativeFSLockFactory f2 = new NativeFSLockFactory(SupportClass.AppSettings.Get("tempDir", altTempDir)); f.SetLockPrefix("test"); Lock l = f.MakeLock("commit"); Lock l2 = f.MakeLock("commit"); Assert.IsTrue(l.Obtain(), "failed to obtain lock"); Assert.IsTrue(!l2.Obtain(), "succeeded in obtaining lock twice"); l.Release(); Assert.IsTrue(l2.Obtain(), "failed to obtain 2nd lock after first one was freed"); l2.Release(); // Make sure we can obtain first one again: Assert.IsTrue(l.Obtain(), "failed to obtain lock"); l.Release(); } // Verify: NativeFSLockFactory assigns different lock // prefixes to different directories: [Test] public virtual void TestNativeFSLockFactoryPrefix() { // Make sure we get identical instances: Directory dir1 = FSDirectory.GetDirectory("TestLockFactory.8", new NativeFSLockFactory("TestLockFactory.8")); Directory dir2 = FSDirectory.GetDirectory("TestLockFactory.9", new NativeFSLockFactory("TestLockFactory.9")); System.String prefix1 = dir1.GetLockFactory().GetLockPrefix(); System.String prefix2 = dir2.GetLockFactory().GetLockPrefix(); Assert.IsTrue(!prefix1.Equals(prefix2), "Native Lock Factories are incorrectly shared: dir1 and dir2 have same lock prefix '" + prefix1 + "'; they should be different"); RmDir("TestLockFactory.8"); RmDir("TestLockFactory.9"); } // Verify: default LockFactory has no prefix (ie // write.lock is stored in index): [Test] public virtual void TestDefaultFSLockFactoryPrefix() { // Make sure we get null prefix: Directory dir = FSDirectory.GetDirectory("TestLockFactory.10"); System.String prefix = dir.GetLockFactory().GetLockPrefix(); Assert.IsTrue(null == prefix, "Default lock prefix should be null"); RmDir("TestLockFactory.10"); } private class WriterThread : SupportClass.ThreadClass { private void InitBlock(TestLockFactory enclosingInstance) { this.enclosingInstance = enclosingInstance; } private TestLockFactory enclosingInstance; public TestLockFactory Enclosing_Instance { get { return enclosingInstance; } } private Directory dir; private int numIteration; public bool hitException = false; public WriterThread(TestLockFactory enclosingInstance, int numIteration, Directory dir) { InitBlock(enclosingInstance); this.numIteration = numIteration; this.dir = dir; } override public void Run() { WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer(); IndexWriter writer = null; for (int i = 0; i < this.numIteration; i++) { try { writer = new IndexWriter(dir, analyzer, false); } catch (System.IO.IOException e) { if (e.ToString().IndexOf(" timed out:") == - 1) { hitException = true; } 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 (System.Exception e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Writer: creation hit unexpected exception: " + e.ToString()); System.Console.Error.WriteLine(e.StackTrace); break; } if (writer != null) { try { Enclosing_Instance.AddDoc(writer); } catch (System.IO.IOException e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Writer: AddDoc hit unexpected exception: " + e.ToString()); System.Console.Error.WriteLine(e.StackTrace); break; } try { writer.Close(); } catch (System.IO.IOException e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Writer: close hit unexpected exception: " + e.ToString()); System.Console.Error.WriteLine(e.StackTrace); break; } writer = null; } } } } private class SearcherThread : SupportClass.ThreadClass { private void InitBlock(TestLockFactory enclosingInstance) { this.enclosingInstance = enclosingInstance; } private TestLockFactory enclosingInstance; public TestLockFactory Enclosing_Instance { get { return enclosingInstance; } } private Directory dir; private int numIteration; public bool hitException = false; public SearcherThread(TestLockFactory enclosingInstance, int numIteration, Directory dir) { InitBlock(enclosingInstance); this.numIteration = numIteration; this.dir = dir; } override public void Run() { IndexSearcher searcher = null; WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer(); Query query = new TermQuery(new Term("content", "aaa")); for (int i = 0; i < this.numIteration; i++) { try { searcher = new IndexSearcher(dir); } catch (System.Exception e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Searcher: create hit unexpected exception: " + e.ToString()); System.Console.Error.WriteLine(e.StackTrace); break; } if (searcher != null) { Hits hits = null; try { hits = searcher.Search(query); } catch (System.IO.IOException e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Searcher: search hit unexpected exception: " + e.ToString()); System.Console.Error.WriteLine(e.StackTrace); break; } // System.out.println(hits.length() + " total results"); try { searcher.Close(); } catch (System.IO.IOException e) { hitException = true; System.Console.Out.WriteLine("Stress Test Index Searcher: close hit unexpected exception: " + e.ToString()); System.Console.Out.WriteLine(e.StackTrace); break; } searcher = null; } } } } public class MockLockFactory : LockFactory { public MockLockFactory(TestLockFactory enclosingInstance) { InitBlock(enclosingInstance); } private void InitBlock(TestLockFactory enclosingInstance) { this.enclosingInstance = enclosingInstance; } private TestLockFactory enclosingInstance; public TestLockFactory Enclosing_Instance { get { return enclosingInstance; } } public bool lockPrefixSet; public System.Collections.Hashtable locksCreated = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); public int makeLockCount = 0; public override void SetLockPrefix(System.String lockPrefix) { base.SetLockPrefix(lockPrefix); lockPrefixSet = true; } public override Lock MakeLock(System.String lockName) { lock (this) { Lock lock_Renamed = new MockLock(this); locksCreated[lockName] = lock_Renamed; makeLockCount++; return lock_Renamed; } } public override void ClearLock(System.String specificLockName) { } public class MockLock : Lock { public MockLock(MockLockFactory enclosingInstance) { InitBlock(enclosingInstance); } private void InitBlock(MockLockFactory enclosingInstance) { this.enclosingInstance = enclosingInstance; } private MockLockFactory enclosingInstance; public MockLockFactory Enclosing_Instance { get { return enclosingInstance; } } public int lockAttempts; public override bool Obtain() { lockAttempts++; return true; } public override void Release() { // do nothing } public override bool IsLocked() { return false; } } } private void AddDoc(IndexWriter writer) { Document doc = new Document(); doc.Add(new Field("content", "aaa", Field.Store.NO, Field.Index.TOKENIZED)); writer.AddDocument(doc); } private void RmDir(System.String dirName) { System.IO.FileInfo dir = new System.IO.FileInfo(dirName); System.String[] files = System.IO.Directory.GetFileSystemEntries(dir.FullName); // clear old files for (int i = 0; i < files.Length; i++) { System.IO.FileInfo file = new System.IO.FileInfo(files[i]); bool tmpBool; if (System.IO.File.Exists(file.FullName)) { System.IO.File.Delete(file.FullName); tmpBool = true; } else if (System.IO.Directory.Exists(file.FullName)) { System.IO.Directory.Delete(file.FullName); tmpBool = true; } else tmpBool = false; bool generatedAux = tmpBool; } bool tmpBool2; if (System.IO.File.Exists(dir.FullName)) { System.IO.File.Delete(dir.FullName); tmpBool2 = true; } else if (System.IO.Directory.Exists(dir.FullName)) { System.IO.Directory.Delete(dir.FullName); tmpBool2 = true; } else tmpBool2 = false; bool generatedAux2 = tmpBool2; } } }