LUCENE-4870: Lucene deletes entire index if and exception is thrown due do TooManyOpenFiles and OpenMode.CREATE_OR_APPEND

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene_solr_4_2@1459909 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 1598dc4..029fbd0 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -32,6 +32,11 @@
 * SOLR-4589: Fixed CPU spikes and poor performance in lazy field loading 
   of multivalued fields. (hossman)
 
+* LUCENE-4870: Fix bug where an entire index might be deleted by the IndexWriter
+  due to false detection if an index exists in the directory when
+  OpenMode.CREATE_OR_APPEND is used. This might also affect application that set
+  the open mode manually using DirectoryReader#indexExists. (Simon Willnauer)
+
 Optimizations
 
 * LUCENE-4819: Added Sorted[Set]DocValues.termsEnum(), and optimized the
diff --git a/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java b/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java
index b173c43..68dd78d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java
@@ -23,6 +23,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.lucene.index.SegmentInfos.FindSegmentsFile;
 import org.apache.lucene.search.SearcherManager; // javadocs
 import org.apache.lucene.store.Directory;
 
@@ -319,7 +320,24 @@
    */
   public static boolean indexExists(Directory directory) {
     try {
-      new SegmentInfos().read(directory);
+      new FindSegmentsFile(directory) {
+        @Override
+        protected Object doBody(String segmentFileName) throws IOException {
+          try {
+            new SegmentInfos().read(directory, segmentFileName);
+          } catch (FileNotFoundException ex) {
+            if (!directory.fileExists(segmentFileName)) {
+              throw ex;
+            }
+            /* this is ok - we might have run into a access exception here.
+             * or even worse like on LUCENE-4870 this is triggered due to
+             * too many open files on the system. In that case we rather report
+             * a false positive here since wrongly returning false from indexExist
+             * can cause data loss since IW relies on this.*/
+          }
+          return null;
+        }
+      }.run();
       return true;
     } catch (IOException ioe) {
       return false;
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java b/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
index d681db9..c3bf7b7 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
@@ -1633,4 +1634,75 @@
     iw.close();
     dir.close();
   }
+  
+  // See LUCENE-4870 TooManyOpenFiles errors are thrown as
+  // FNFExceptions which can trigger data loss.
+  public void testTooManyFileException() throws Exception {
+
+    // Create failure that throws Too many open files exception randomly
+    MockDirectoryWrapper.Failure failure = new MockDirectoryWrapper.Failure() {
+
+      @Override
+      public MockDirectoryWrapper.Failure reset() {
+        doFail = false;
+        return this;
+      }
+
+      @Override
+      public void eval(MockDirectoryWrapper dir) throws IOException {
+        if (doFail) {
+          if (random().nextBoolean()) {
+            throw new FileNotFoundException("some/file/name.ext (Too many open files)");
+          }
+        }
+      }
+    };
+
+    MockDirectoryWrapper dir = newMockDirectory();
+    // The exception is only thrown on open input
+    dir.setFailOnOpenInput(true);
+    dir.failOn(failure);
+
+    // Create an index with one document
+    IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()));
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    doc.add(new StringField("foo", "bar", Field.Store.NO));
+    iw.addDocument(doc); // add a document
+    iw.commit();
+    DirectoryReader ir = DirectoryReader.open(dir);
+    assertEquals(1, ir.numDocs());
+    ir.close();
+    iw.close();
+
+    // Open and close the index a few times
+    for (int i = 0; i < 10; i++) {
+      failure.setDoFail();
+      iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()));
+      try {
+        iw = new IndexWriter(dir, iwc);
+      } catch (CorruptIndexException ex) {
+        // Exceptions are fine - we are running out of file handlers here
+        continue;
+      } catch (FileNotFoundException ex) {
+        continue;
+      }
+      failure.clearDoFail();
+      iw.close();
+      ir = DirectoryReader.open(dir);
+      assertEquals("lost document after iteration: " + i, 1, ir.numDocs());
+      ir.close();
+    }
+
+    // Check if document is still there
+    failure.clearDoFail();
+    ir = DirectoryReader.open(dir);
+    assertEquals(1, ir.numDocs());
+    ir.close();
+
+    dir.close();
+  }
+  
+  
+
 }