lucene-java-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yo...@apache.org
Subject svn commit: r464611 - in /lucene/java/trunk: ./ src/java/org/apache/lucene/index/ src/java/org/apache/lucene/store/ src/test/org/apache/lucene/store/
Date Mon, 16 Oct 2006 18:28:04 GMT
Author: yonik
Date: Mon Oct 16 11:28:02 2006
New Revision: 464611

URL: http://svn.apache.org/viewvc?view=rev&rev=464611
Log:
NativeFSLockFactory: LUCENE-678

Added:
    lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java   (with props)
Modified:
    lucene/java/trunk/CHANGES.txt
    lucene/java/trunk/src/java/org/apache/lucene/index/FieldsReader.java
    lucene/java/trunk/src/java/org/apache/lucene/store/FSDirectory.java
    lucene/java/trunk/src/java/org/apache/lucene/store/Lock.java
    lucene/java/trunk/src/java/org/apache/lucene/store/LockFactory.java
    lucene/java/trunk/src/java/org/apache/lucene/store/SimpleFSLockFactory.java
    lucene/java/trunk/src/test/org/apache/lucene/store/TestLockFactory.java

Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Mon Oct 16 11:28:02 2006
@@ -33,6 +33,10 @@
 
  3. LUCENE-676: Move Solr's PrefixFilter to Lucene core. (Yura Smolsky, Yonik Seeley)
 
+ 4. LUCENE-678: Added NativeFSLockFactory, which implements locking
+    using OS native locking (via java.nio.*).  (Michael McCandless via Yonik Seeley)
+
+
 API Changes
 
  1. LUCENE-438: Remove "final" from Token, implement Cloneable, allow

Modified: lucene/java/trunk/src/java/org/apache/lucene/index/FieldsReader.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/index/FieldsReader.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/index/FieldsReader.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/index/FieldsReader.java Mon Oct 16 11:28:02
2006
@@ -55,7 +55,7 @@
   }
 
   /**
-   * Cloeses the underlying {@link org.apache.lucene.store.IndexInput} streams, including
any ones associated with a
+   * Closes the underlying {@link org.apache.lucene.store.IndexInput} streams, including
any ones associated with a
    * lazy implementation of a Field.  This means that the Fields values will not be accessible.
    *
    * @throws IOException

Modified: lucene/java/trunk/src/java/org/apache/lucene/store/FSDirectory.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/store/FSDirectory.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/store/FSDirectory.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/store/FSDirectory.java Mon Oct 16 11:28:02
2006
@@ -228,8 +228,8 @@
 
     // Set up lockFactory with cascaded defaults: if an instance was passed in,
     // use that; else if locks are disabled, use NoLockFactory; else if the
-    // system property org.apache.lucene.lockClass is set, instantiate that;
-    // else, use SimpleFSLockFactory:
+    // system property org.apache.lucene.store.FSDirectoryLockFactoryClass is set,
+    // instantiate that; else, use SimpleFSLockFactory:
 
     if (lockFactory == null) {
 

Modified: lucene/java/trunk/src/java/org/apache/lucene/store/Lock.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/store/Lock.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/store/Lock.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/store/Lock.java Mon Oct 16 11:28:02 2006
@@ -40,6 +40,13 @@
    */
   public abstract boolean obtain() throws IOException;
 
+  /**
+   * If a lock obtain called, this failureReason may be set
+   * with the "root cause" Exception as to why the lock was
+   * not obtained.
+   */
+  protected Throwable failureReason;
+
   /** Attempts to obtain an exclusive lock within amount
    *  of time given. Currently polls once per second until
    *  lockWaitTimeout is passed.
@@ -48,12 +55,21 @@
    * @throws IOException if lock wait times out or obtain() throws an IOException
    */
   public boolean obtain(long lockWaitTimeout) throws IOException {
+    failureReason = null;
     boolean locked = obtain();
     int maxSleepCount = (int)(lockWaitTimeout / LOCK_POLL_INTERVAL);
     int sleepCount = 0;
     while (!locked) {
       if (sleepCount++ == maxSleepCount) {
-        throw new IOException("Lock obtain timed out: " + this.toString());
+        String reason = "Lock obtain timed out: " + this.toString();
+        if (failureReason != null) {
+          reason += ": " + failureReason;
+        }
+        IOException e = new IOException(reason);
+        if (failureReason != null) {
+          e.initCause(failureReason);
+        }
+        throw e;
       }
       try {
         Thread.sleep(LOCK_POLL_INTERVAL);

Modified: lucene/java/trunk/src/java/org/apache/lucene/store/LockFactory.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/store/LockFactory.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/store/LockFactory.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/store/LockFactory.java Mon Oct 16 11:28:02
2006
@@ -28,7 +28,14 @@
   protected String lockPrefix = "";
 
   /**
-   * Set the prefix in use for all locks created in this LockFactory.
+   * Set the prefix in use for all locks created in this
+   * LockFactory.  This is normally called once, when a
+   * Directory gets this LockFactory instance.  However, you
+   * can also call this (after this instance is assigned to
+   * a Directory) to override the prefix in use.  This
+   * is helpful if you're running Lucene on machines that
+   * have different mount points for the same shared
+   * directory.
    */
   public void setLockPrefix(String lockPrefix) {
     this.lockPrefix = lockPrefix;

Added: lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java?view=auto&rev=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java (added)
+++ lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java Mon Oct 16
11:28:02 2006
@@ -0,0 +1,313 @@
+package org.apache.lucene.store;
+
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Random;
+
+/**
+ * Implements {@link LockFactory} using native OS file locks
+ * (available through java.nio.*).  Note that for certain
+ * filesystems native locks are possible but must be
+ * explicity configured and enabled (and may be disabled by
+ * default).  For example, for NFS servers there sometimes
+ * must be a separate lockd process running, and other
+ * configuration may be required such as running the server
+ * in kernel mode.  Other filesystems may not even support
+ * native OS locks in which case you must use a different
+ * {@link LockFactory} implementation.
+ *
+ * <p>The advantage of this lock factory over
+ * SimpleFSLockFactory is that the locks should be
+ * "correct", whereas SimpleFSLockFactory uses
+ * java.io.File.createNewFile which has warnings about not
+ * using it for locking.  Furthermore, if the JVM crashes,
+ * the OS will free any held locks, whereas
+ * SimpleFSLockFactory will keep the locks held, requiring
+ * manual removal before re-running Lucene.</p>
+ *
+ * <p>Note that, unlike SimpleFSLockFactory, the existence of
+ * leftover lock files in the filesystem on exiting the JVM
+ * is fine because the OS will free the locks held against
+ * these files even though the files still remain.</p>
+ *
+ * <p>Native locks file names have the substring "-n-", which
+ * you can use to differentiate them from lock files created
+ * by SimpleFSLockFactory.</p>
+ *
+ * @see LockFactory
+ */
+
+public class NativeFSLockFactory extends LockFactory {
+
+  /**
+   * Directory specified by <code>org.apache.lucene.lockDir</code>
+   * system property.  If that is not set, then <code>java.io.tmpdir</code>
+   * system property is used.
+   */
+
+  public static final String LOCK_DIR =
+    System.getProperty("org.apache.lucene.lockDir",
+                       System.getProperty("java.io.tmpdir"));
+
+  private File lockDir;
+
+  /*
+   * The javadocs for FileChannel state that you should have
+   * a single instance of a FileChannel (per JVM) for all
+   * locking against a given file.  To do this, we ensure
+   * there's a single instance of NativeFSLockFactory per
+   * canonical lock directory, and then we always use a
+   * single lock instance (per lock name) if it's present.
+   */
+  private static Hashtable LOCK_FACTORIES = new Hashtable();
+
+  private Hashtable locks = new Hashtable();
+
+  protected NativeFSLockFactory(File lockDir)
+    throws IOException {
+
+    this.lockDir = lockDir;
+
+    // Ensure that lockDir exists and is a directory.
+    if (!lockDir.exists()) {
+      if (!lockDir.mkdirs())
+        throw new IOException("Cannot create directory: " +
+                              lockDir.getAbsolutePath());
+    } else if (!lockDir.isDirectory()) {
+      throw new IOException("Found regular file where directory expected: " + 
+                            lockDir.getAbsolutePath());
+    }
+    acquireTestLock();
+  }
+
+  // Simple test to verify locking system is "working".  On
+  // NFS, if it's misconfigured, you can hit long (35
+  // second) timeouts which cause Lock.obtain to take far
+  // too long (it assumes the obtain() call takes zero
+  // time).  Since it's a configuration problem, we test up
+  // front once on creating the LockFactory:
+  private void acquireTestLock() throws IOException {
+    String randomLockName = "lucene-" + Long.toString(new Random().nextInt(), Character.MAX_RADIX)
+ "-test.lock";
+    
+    Lock l = makeLock(randomLockName);
+    try {
+      l.obtain();
+    } catch (IOException e) {
+      IOException e2 = new IOException("Failed to acquire random test lock; please verify
filesystem for lock directory '" + lockDir + "' supports locking");
+      e2.initCause(e);
+      throw e2;
+    }
+
+    l.release();
+  }
+
+  /**
+   * Returns a NativeFSLockFactory instance, storing lock
+   * files into the default LOCK_DIR:
+   * <code>org.apache.lucene.lockDir</code> system property,
+   * or (if that is null) then <code>java.io.tmpdir</code>.
+   */
+  public static NativeFSLockFactory getLockFactory() throws IOException {
+    return getLockFactory(new File(LOCK_DIR));
+  }
+
+  /**
+   * Returns a NativeFSLockFactory instance, storing lock
+   * files into the specified lockDirName:
+   *
+   * @param lockDirName where lock files are created.
+   */
+  public static NativeFSLockFactory getLockFactory(String lockDirName) throws IOException
{
+    return getLockFactory(new File(lockDirName));
+  }
+
+  /**
+   * Returns a NativeFSLockFactory instance, storing lock
+   * files into the specified lockDir:
+   * 
+   * @param lockDir where lock files are created.
+   */
+  public static NativeFSLockFactory getLockFactory(File lockDir) throws IOException {
+    lockDir = new File(lockDir.getCanonicalPath());
+
+    NativeFSLockFactory f;
+
+    synchronized(LOCK_FACTORIES) {
+      f = (NativeFSLockFactory) LOCK_FACTORIES.get(lockDir);
+      if (f == null) {
+        f = new NativeFSLockFactory(lockDir);
+        LOCK_FACTORIES.put(lockDir, f);
+      }
+    }
+
+    return f;
+  }
+
+  public synchronized Lock makeLock(String lockName) {
+    Lock l = (Lock) locks.get(lockName);
+    if (l == null) {
+      String fullName;
+      if (lockPrefix.equals("")) {
+        fullName = lockName;
+      } else {
+        fullName = lockPrefix + "-n-" + lockName;
+      }
+
+      l = new NativeFSLock(lockDir, fullName);
+      locks.put(lockName, l);
+    }
+    return l;
+  }
+
+  public void clearAllLocks() throws IOException {
+    // Note that this isn't strictly required anymore
+    // because the existence of these files does not mean
+    // they are locked, but, still do this in case people
+    // really want to see the files go away:
+    if (lockDir.exists()) {
+        String[] files = lockDir.list();
+        if (files == null)
+          throw new IOException("Cannot read lock directory " +
+                                lockDir.getAbsolutePath());
+        String prefix = lockPrefix + "-n-";
+        for (int i = 0; i < files.length; i++) {
+          if (files[i].startsWith(prefix)) {
+            File lockFile = new File(lockDir, files[i]);
+            if (!lockFile.delete())
+              throw new IOException("Cannot delete " + lockFile);
+          }
+        }
+    }
+  }
+};
+
+class NativeFSLock extends Lock {
+
+  private RandomAccessFile f;
+  private FileChannel channel;
+  private FileLock lock;
+  private File path;
+  private File lockDir;
+
+  public NativeFSLock(File lockDir, String lockFileName) {
+    this.lockDir = lockDir;
+    path = new File(lockDir, lockFileName);
+  }
+
+  public synchronized boolean obtain() throws IOException {
+
+    if (isLocked()) {
+      // We are already locked:
+      return false;
+    }
+
+    // Ensure that lockDir exists and is a directory.
+    if (!lockDir.exists()) {
+      if (!lockDir.mkdirs())
+        throw new IOException("Cannot create directory: " +
+                              lockDir.getAbsolutePath());
+    } else if (!lockDir.isDirectory()) {
+      throw new IOException("Found regular file where directory expected: " + 
+                            lockDir.getAbsolutePath());
+    }
+
+    f = new RandomAccessFile(path, "rw");
+    try {
+      channel = f.getChannel();
+      try {
+        try {
+          lock = channel.tryLock();
+        } catch (IOException e) {
+          // At least on OS X, we will sometimes get an
+          // intermittant "Permission Denied" IOException,
+          // which seems to simply mean "you failed to get
+          // the lock".  But other IOExceptions could be
+          // "permanent" (eg, locking is not supported via
+          // the filesystem).  So, we record the failure
+          // reason here; the timeout obtain (usually the
+          // one calling us) will use this as "root cause"
+          // if it fails to get the lock.
+          failureReason = e;
+        }
+      } finally {
+        if (lock == null) {
+          try {
+            channel.close();
+          } finally {
+            channel = null;
+          }
+        }
+      }
+    } finally {
+      if (channel == null) {
+        try {
+          f.close();
+        } finally {
+          f = null;
+        }
+      }
+    }
+    return lock != null;
+  }
+
+  public synchronized void release() {
+    try {
+      if (isLocked()) {
+        try {
+          lock.release();
+        } finally {
+          lock = null;
+          try {
+            channel.close();
+          } finally {
+            channel = null;
+            try {
+              f.close();
+            } finally {
+              f = null;
+            }
+          }
+        }
+        path.delete();
+      }
+    } catch (IOException e) {
+      // Not sure how to better message/handle this without
+      // changing API?
+      throw new RuntimeException(e);
+    }
+  }
+
+  public boolean isLocked() {
+    return lock != null;
+  }
+
+  public String toString() {
+    return "NativeFSLock@" + path;
+  }
+
+  public void finalize() {
+    if (isLocked()) {
+      release();
+    }
+  }
+}

Propchange: lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/java/trunk/src/java/org/apache/lucene/store/NativeFSLockFactory.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: lucene/java/trunk/src/java/org/apache/lucene/store/SimpleFSLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/store/SimpleFSLockFactory.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/store/SimpleFSLockFactory.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/store/SimpleFSLockFactory.java Mon Oct 16
11:28:02 2006
@@ -80,17 +80,19 @@
   }
 
   public void clearAllLocks() throws IOException {
-    String[] files = lockDir.list();
-    if (files == null)
-      throw new IOException("Cannot read lock directory " +
-                            lockDir.getAbsolutePath());
-    String prefix = lockPrefix + "-";
-    for (int i = 0; i < files.length; i++) {
-      if (!files[i].startsWith(prefix))
-        continue;
-      File lockFile = new File(lockDir, files[i]);
-      if (!lockFile.delete())
-        throw new IOException("Cannot delete " + lockFile);
+    if (lockDir.exists()) {
+        String[] files = lockDir.list();
+        if (files == null)
+          throw new IOException("Cannot read lock directory " +
+                                lockDir.getAbsolutePath());
+        String prefix = lockPrefix + "-";
+        for (int i = 0; i < files.length; i++) {
+          if (!files[i].startsWith(prefix))
+            continue;
+          File lockFile = new File(lockDir, files[i]);
+          if (!lockFile.delete())
+            throw new IOException("Cannot delete " + lockFile);
+        }
     }
   }
 };

Modified: lucene/java/trunk/src/test/org/apache/lucene/store/TestLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/store/TestLockFactory.java?view=diff&rev=464611&r1=464610&r2=464611
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/store/TestLockFactory.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/store/TestLockFactory.java Mon Oct 16 11:28:02
2006
@@ -130,7 +130,8 @@
         IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true);
 
         assertTrue("FSDirectory did not use correct LockFactory: got " + writer.getDirectory().getLockFactory(),
-                   SimpleFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory()));
+                   SimpleFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory())
||
+                   NativeFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory()));
 
         IndexWriter writer2 = null;
 
@@ -157,7 +158,10 @@
         IndexWriter writer = new IndexWriter(indexDirName, new WhitespaceAnalyzer(), true);
 
         assertTrue("FSDirectory did not use correct LockFactory: got " + writer.getDirectory().getLockFactory(),
-                   SimpleFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory()));
+                   SimpleFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory())
||
+                   NativeFSLockFactory.class.isInstance(writer.getDirectory().getLockFactory()));
+
+        writer.close();
 
         // Create a 2nd IndexWriter.  This should not fail:
         IndexWriter writer2 = null;
@@ -218,10 +222,6 @@
             fail("Should not have hit an IOException with locking disabled");
         }
 
-        // Put back to the correct default for subsequent tests:
-        System.setProperty("org.apache.lucene.store.FSDirectoryLockFactoryClass",
-                           "org.apache.lucene.store.SimpleFSLockFactory");
-
         FSDirectory.setDisableLocks(false);
         writer.close();
         if (writer2 != null) {
@@ -266,9 +266,19 @@
     // IndexWriters over & over in 2 threads and making sure
     // no unexpected exceptions are raised:
     public void testStressLocks() throws IOException {
+      _testStressLocks(null, "index.TestLockFactory6");
+    }
 
-        String indexDirName = "index.TestLockFactory6";
-        FSDirectory fs1 = FSDirectory.getDirectory(indexDirName, true);
+    // 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 IOException {
+      _testStressLocks(NativeFSLockFactory.getLockFactory(), "index.TestLockFactory7");
+    }
+
+    public void _testStressLocks(LockFactory lockFactory, String indexDirName) throws IOException
{
+        FSDirectory fs1 = FSDirectory.getDirectory(indexDirName, true, lockFactory);
         // fs1.setLockFactory(NoLockFactory.getNoLockFactory());
 
         // First create a 1 doc index:
@@ -293,6 +303,31 @@
 
         // Cleanup
         rmDir(indexDirName);
+    }
+
+    // Verify: NativeFSLockFactory works correctly
+    public void testNativeFSLockFactory() throws IOException {
+
+      // Make sure we get identical instances:
+      NativeFSLockFactory f = NativeFSLockFactory.getLockFactory();
+      NativeFSLockFactory f2 = NativeFSLockFactory.getLockFactory();
+      assertTrue("got different NativeFSLockFactory instances for same directory",
+                 f == f2);
+
+      // Make sure we get identical locks:
+      f.setLockPrefix("test");
+      Lock l = f.makeLock("commit");
+      Lock l2 = f.makeLock("commit");
+      assertTrue("got different Lock instances for same lock name",
+                 l == l2);
+
+      assertTrue("failed to obtain lock", l.obtain());
+      assertTrue("succeeded in obtaining lock twice", !l.obtain());
+      l.release();
+
+      // Make sure we can obtain it again:
+      assertTrue("failed to obtain lock", l.obtain());
+      l.release();
     }
 
     private class WriterThread extends Thread { 



Mime
View raw message