| package org.apache.lucene.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. |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.index.IndexWriter; |
| import org.apache.lucene.index.IndexWriterConfig; |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.index.IndexWriterConfig.OpenMode; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.TermQuery; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util._TestUtil; |
| |
| public class TestLockFactory extends LuceneTestCase { |
| |
| // Verify: we can provide our own LockFactory implementation, the right |
| // methods are called at the right time, locks are created, etc. |
| |
| public void testCustomLockFactory() throws IOException { |
| Directory dir = new MockDirectoryWrapper(random, new RAMDirectory()); |
| MockLockFactory lf = new MockLockFactory(); |
| dir.setLockFactory(lf); |
| |
| // Lock prefix should have been set: |
| assertTrue("lock prefix was not set by the RAMDirectory", lf.lockPrefixSet); |
| |
| 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: |
| assertEquals("# of unique locks created (after instantiating IndexWriter)", |
| 1, lf.locksCreated.size()); |
| assertTrue("# calls to makeLock is 0 (after instantiating IndexWriter)", |
| lf.makeLockCount >= 1); |
| |
| for(final String lockName : lf.locksCreated.keySet()) { |
| MockLockFactory.MockLock lock = (MockLockFactory.MockLock) lf.locksCreated.get(lockName); |
| assertTrue("# calls to Lock.obtain is 0 (after instantiating IndexWriter)", |
| lock.lockAttempts > 0); |
| } |
| |
| writer.close(); |
| } |
| |
| // Verify: we can use the NoLockFactory with RAMDirectory w/ no |
| // exceptions raised: |
| // Verify: NoLockFactory allows two IndexWriters |
| public void testRAMDirectoryNoLocking() throws IOException { |
| Directory dir = new MockDirectoryWrapper(random, new RAMDirectory()); |
| dir.setLockFactory(NoLockFactory.getNoLockFactory()); |
| |
| assertTrue("RAMDirectory.setLockFactory did not take", |
| NoLockFactory.class.isInstance(dir.getLockFactory())); |
| |
| 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) { |
| e.printStackTrace(System.out); |
| 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) |
| public void testDefaultRAMDirectory() throws IOException { |
| Directory dir = new RAMDirectory(); |
| |
| assertTrue("RAMDirectory did not use correct LockFactory: got " + dir.getLockFactory(), |
| SingleInstanceLockFactory.class.isInstance(dir.getLockFactory())); |
| |
| 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)); |
| fail("Should have hit an IOException with two IndexWriters on default SingleInstanceLockFactory"); |
| } catch (IOException e) { |
| } |
| |
| writer.close(); |
| if (writer2 != null) { |
| writer2.close(); |
| } |
| } |
| |
| public void testSimpleFSLockFactory() throws IOException { |
| // 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: |
| public void testStressLocks() throws Exception { |
| _testStressLocks(null, _TestUtil.getTempDir("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 void testStressLocksNativeFSLockFactory() throws Exception { |
| File dir = _TestUtil.getTempDir("index.TestLockFactory7"); |
| _testStressLocks(new NativeFSLockFactory(dir), dir); |
| } |
| |
| public void _testStressLocks(LockFactory lockFactory, File indexDir) throws Exception { |
| 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.close(); |
| |
| WriterThread writer = new WriterThread(100, dir); |
| SearcherThread searcher = new SearcherThread(100, dir); |
| writer.start(); |
| searcher.start(); |
| |
| while(writer.isAlive() || searcher.isAlive()) { |
| Thread.sleep(1000); |
| } |
| |
| assertTrue("IndexWriter hit unexpected exceptions", !writer.hitException); |
| assertTrue("IndexSearcher hit unexpected exceptions", !searcher.hitException); |
| |
| dir.close(); |
| // Cleanup |
| _TestUtil.rmDir(indexDir); |
| } |
| |
| // Verify: NativeFSLockFactory works correctly |
| public void testNativeFSLockFactory() throws IOException { |
| |
| NativeFSLockFactory f = new NativeFSLockFactory(TEMP_DIR); |
| |
| f.setLockPrefix("test"); |
| Lock l = f.makeLock("commit"); |
| Lock l2 = f.makeLock("commit"); |
| |
| assertTrue("failed to obtain lock", l.obtain()); |
| assertTrue("succeeded in obtaining lock twice", !l2.obtain()); |
| l.release(); |
| |
| assertTrue("failed to obtain 2nd lock after first one was freed", l2.obtain()); |
| l2.release(); |
| |
| // Make sure we can obtain first one again, test isLocked(): |
| assertTrue("failed to obtain lock", l.obtain()); |
| assertTrue(l.isLocked()); |
| assertTrue(l2.isLocked()); |
| l.release(); |
| assertFalse(l.isLocked()); |
| assertFalse(l2.isLocked()); |
| } |
| |
| |
| // Verify: NativeFSLockFactory works correctly if the lock file exists |
| public void testNativeFSLockFactoryLockExists() throws IOException { |
| |
| File lockFile = new File(TEMP_DIR, "test.lock"); |
| lockFile.createNewFile(); |
| |
| Lock l = new NativeFSLockFactory(TEMP_DIR).makeLock("test.lock"); |
| assertTrue("failed to obtain lock", l.obtain()); |
| l.release(); |
| assertFalse("failed to release lock", l.isLocked()); |
| if (lockFile.exists()) { |
| lockFile.delete(); |
| } |
| } |
| |
| public void testNativeFSLockReleaseByOtherLock() throws IOException { |
| |
| NativeFSLockFactory f = new NativeFSLockFactory(TEMP_DIR); |
| |
| f.setLockPrefix("test"); |
| Lock l = f.makeLock("commit"); |
| Lock l2 = f.makeLock("commit"); |
| |
| assertTrue("failed to obtain lock", l.obtain()); |
| try { |
| assertTrue(l2.isLocked()); |
| l2.release(); |
| fail("should not have reached here. LockReleaseFailedException should have been thrown"); |
| } catch (LockReleaseFailedException e) { |
| // expected |
| } finally { |
| l.release(); |
| } |
| } |
| |
| // Verify: NativeFSLockFactory assigns null as lockPrefix if the lockDir is inside directory |
| public void testNativeFSLockFactoryPrefix() throws IOException { |
| File fdir1 = _TestUtil.getTempDir("TestLockFactory.8"); |
| File fdir2 = _TestUtil.getTempDir("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.getLockFactory().getLockPrefix(); |
| assertNull("Lock prefix for lockDir same as directory should be null", prefix1); |
| |
| String prefix2 = dir2.getLockFactory().getLockPrefix(); |
| assertNotNull("Lock prefix for lockDir outside of directory should be not null", prefix2); |
| |
| dir1.close(); |
| dir2.close(); |
| |
| _TestUtil.rmDir(fdir1); |
| _TestUtil.rmDir(fdir2); |
| } |
| |
| // Verify: default LockFactory has no prefix (ie |
| // write.lock is stored in index): |
| public void testDefaultFSLockFactoryPrefix() throws IOException { |
| // Make sure we get null prefix, which wont happen if setLockFactory is ever called. |
| File dirName = _TestUtil.getTempDir("TestLockFactory.10"); |
| |
| Directory dir = new SimpleFSDirectory(dirName); |
| assertNull("Default lock prefix should be null", dir.getLockFactory().getLockPrefix()); |
| dir.close(); |
| |
| dir = new MMapDirectory(dirName); |
| assertNull("Default lock prefix should be null", dir.getLockFactory().getLockPrefix()); |
| dir.close(); |
| |
| dir = new NIOFSDirectory(dirName); |
| assertNull("Default lock prefix should be null", dir.getLockFactory().getLockPrefix()); |
| dir.close(); |
| |
| _TestUtil.rmDir(dirName); |
| } |
| |
| private class WriterThread extends Thread { |
| private Directory dir; |
| private int numIteration; |
| public boolean hitException = false; |
| public WriterThread(int numIteration, Directory dir) { |
| this.numIteration = numIteration; |
| this.dir = dir; |
| } |
| @Override |
| public 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:") == -1) { |
| hitException = true; |
| System.out.println("Stress Test Index Writer: creation hit unexpected IOException: " + e.toString()); |
| e.printStackTrace(System.out); |
| } 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; |
| System.out.println("Stress Test Index Writer: creation hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| if (writer != null) { |
| try { |
| addDoc(writer); |
| } catch (IOException e) { |
| hitException = true; |
| System.out.println("Stress Test Index Writer: addDoc hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| try { |
| writer.close(); |
| } catch (IOException e) { |
| hitException = true; |
| System.out.println("Stress Test Index Writer: close hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| writer = null; |
| } |
| } |
| } |
| } |
| |
| private class SearcherThread extends Thread { |
| private Directory dir; |
| private int numIteration; |
| public boolean hitException = false; |
| public SearcherThread(int numIteration, Directory dir) { |
| this.numIteration = numIteration; |
| this.dir = dir; |
| } |
| @Override |
| public void run() { |
| IndexSearcher searcher = null; |
| Query query = new TermQuery(new Term("content", "aaa")); |
| for(int i=0;i<this.numIteration;i++) { |
| try{ |
| searcher = new IndexSearcher(dir, false); |
| } catch (Exception e) { |
| hitException = true; |
| System.out.println("Stress Test Index Searcher: create hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| try { |
| searcher.search(query, null, 1000); |
| } catch (IOException e) { |
| hitException = true; |
| System.out.println("Stress Test Index Searcher: search hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| // System.out.println(hits.length() + " total results"); |
| try { |
| searcher.close(); |
| } catch (IOException e) { |
| hitException = true; |
| System.out.println("Stress Test Index Searcher: close hit unexpected exception: " + e.toString()); |
| e.printStackTrace(System.out); |
| break; |
| } |
| } |
| } |
| } |
| |
| public class MockLockFactory extends LockFactory { |
| |
| public boolean lockPrefixSet; |
| public Map<String,Lock> locksCreated = Collections.synchronizedMap(new HashMap<String,Lock>()); |
| public int makeLockCount = 0; |
| |
| @Override |
| public void setLockPrefix(String lockPrefix) { |
| super.setLockPrefix(lockPrefix); |
| lockPrefixSet = true; |
| } |
| |
| @Override |
| synchronized public Lock makeLock(String lockName) { |
| Lock lock = new MockLock(); |
| locksCreated.put(lockName, lock); |
| makeLockCount++; |
| return lock; |
| } |
| |
| @Override |
| public void clearLock(String specificLockName) {} |
| |
| public class MockLock extends Lock { |
| public int lockAttempts; |
| |
| @Override |
| public boolean obtain() { |
| lockAttempts++; |
| return true; |
| } |
| @Override |
| public void release() { |
| // do nothing |
| } |
| @Override |
| public boolean isLocked() { |
| return false; |
| } |
| } |
| } |
| |
| private void addDoc(IndexWriter writer) throws IOException { |
| Document doc = new Document(); |
| doc.add(newField("content", "aaa", Field.Store.NO, Field.Index.ANALYZED)); |
| writer.addDocument(doc); |
| } |
| } |