Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 940404) +++ lucene/CHANGES.txt (working copy) @@ -124,6 +124,10 @@ * LUCENE-2179: CharArraySet.clear() is now functional. (Robert Muir, Uwe Schindler) +* LUCENE-2421: NativeFSLockFactory does not throw LockReleaseFailedException if + it cannot delete the lock file, since obtaining the lock does not fail if the + file is there. (Shai Erera) + API Changes * LUCENE-2076: Rename FSDirectory.getFile -> getDirectory. (George Index: lucene/src/java/org/apache/lucene/store/NativeFSLockFactory.java =================================================================== --- lucene/src/java/org/apache/lucene/store/NativeFSLockFactory.java (revision 939554) +++ lucene/src/java/org/apache/lucene/store/NativeFSLockFactory.java (working copy) @@ -17,6 +17,7 @@ * limitations under the License. */ +import java.lang.management.ManagementFactory; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.io.File; @@ -25,6 +26,8 @@ import java.util.HashSet; import java.util.Random; +import org.apache.lucene.util.ThreadInterruptedException; + /** *

Implements {@link LockFactory} using native OS file * locks. Note that because this LockFactory relies on @@ -78,12 +81,26 @@ lockDir.getAbsolutePath()); } - String randomLockName = "lucene-" + Long.toString(new Random().nextInt(), Character.MAX_RADIX) + "-test.lock"; + // add the RuntimeMXBean's name to the lock file, to reduce the chance for + // name collisions when this code is invoked by multiple JVMs (such as in + // our tests). On most systems, the name includes the process Id. + // Also, remove any non-alphanumeric characters, so that the lock file will + // be created for sure on all systems. + String randomLockName = "lucene-" + + ManagementFactory.getRuntimeMXBean().getName().replaceAll("[^a..zA..Z0..9]+","") + "-" + + Long.toString(new Random().nextInt(), Character.MAX_RADIX) + + "-test.lock"; Lock l = makeLock(randomLockName); try { l.obtain(); l.release(); + // If the test lock failed to delete after all the attempts, attempt a + // delete when the JVM exits. + File lockFile = new File(lockDir, randomLockName); + if (lockFile.exists()) { + lockFile.deleteOnExit(); + } } catch (IOException e) { RuntimeException e2 = new RuntimeException("Failed to acquire random test lock; please verify filesystem for lock directory '" + lockDir + "' supports locking"); e2.initCause(e); @@ -308,7 +325,24 @@ } } if (!path.delete()) - throw new LockReleaseFailedException("failed to delete " + path); + // This can happen in several scenarios: + // 1. The lock is released, but the file is being held by an external + // process, such as AntiVirus + // 2. When we acquire the test lock file, it might be (under rare + // circumstances) that two JVMs will use the same lock file, and one + // will succeed to delete the file, while the other will get 'false' + // from path.delete() + // Therefore, we check if the file exists at all, and if so, attempt + // a re-delete few ms later + if (path.exists()) { + try { + wait(100); + } catch (InterruptedException e) { + throw new ThreadInterruptedException(e); + } + // Last chance re-delete, but don't fail. + path.delete(); + } } else { // if we don't hold the lock, and somebody still called release(), for // example as a result of calling IndexWriter.unlock(), we should attempt Index: lucene/src/test/org/apache/lucene/store/TestLockFactory.java =================================================================== --- lucene/src/test/org/apache/lucene/store/TestLockFactory.java (revision 939554) +++ lucene/src/test/org/apache/lucene/store/TestLockFactory.java (working copy) @@ -193,6 +193,22 @@ 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); @@ -206,8 +222,8 @@ assertTrue(l2.isLocked()); l2.release(); fail("should not have reached here. LockReleaseFailedException should have been thrown"); - } catch (IOException e) { - assertTrue("Unexpected exception", e instanceof LockReleaseFailedException); + } catch (LockReleaseFailedException e) { + // expected } finally { l.release(); }