--- /dev/null
+package org.apache.lucene.index;
+
+/**
+ * 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.IOException;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.DefaultSimilarity;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.MockDirectoryWrapper;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util._TestUtil;
+
+public class TestIndexReaderOnDiskFull extends LuceneTestCase {
+ /**
+ * Make sure if reader tries to commit but hits disk
+ * full that reader remains consistent and usable.
+ */
+ public void testDiskFull() throws IOException {
+
+ Term searchTerm = new Term("content", "aaa");
+ int START_COUNT = 157;
+ int END_COUNT = 144;
+
+ // First build up a starting index:
+ MockDirectoryWrapper startDir = newDirectory();
+ IndexWriter writer = new IndexWriter(startDir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ if (VERBOSE) {
+ System.out.println("TEST: create initial index");
+ writer.setInfoStream(System.out);
+ }
+ for(int i=0;i<157;i++) {
+ Document d = new Document();
+ d.add(newField("id", Integer.toString(i), Field.Store.YES, Field.Index.NOT_ANALYZED));
+ d.add(newField("content", "aaa " + i, Field.Store.NO, Field.Index.ANALYZED));
+ writer.addDocument(d);
+ if (0==i%10)
+ writer.commit();
+ }
+ writer.close();
+
+ {
+ IndexReader r = IndexReader.open(startDir);
+ IndexSearcher searcher = newSearcher(r);
+ ScoreDoc[] hits = null;
+ try {
+ hits = searcher.search(new TermQuery(searchTerm), null, 1000).scoreDocs;
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("exception when init searching: " + e);
+ }
+ searcher.close();
+ r.close();
+ }
+
+ long diskUsage = startDir.getRecomputedActualSizeInBytes();
+ long diskFree = diskUsage+_TestUtil.nextInt(random, 50, 200);
+
+ IOException err = null;
+
+ boolean done = false;
+ boolean gotExc = false;
+
+ // Iterate w/ ever increasing free disk space:
+ while(!done) {
+ MockDirectoryWrapper dir = new MockDirectoryWrapper(random, new RAMDirectory(startDir));
+
+ // If IndexReader hits disk full, it can write to
+ // the same files again.
+ dir.setPreventDoubleWrite(false);
+
+ IndexReader reader = IndexReader.open(dir, false);
+
+ // For each disk size, first try to commit against
+ // dir that will hit random IOExceptions & disk
+ // full; after, give it infinite disk space & turn
+ // off random IOExceptions & retry w/ same reader:
+ boolean success = false;
+
+ for(int x=0;x<2;x++) {
+
+ double rate = 0.05;
+ double diskRatio = ((double) diskFree)/diskUsage;
+ long thisDiskFree;
+ String testName;
+
+ if (0 == x) {
+ thisDiskFree = diskFree;
+ if (diskRatio >= 2.0) {
+ rate /= 2;
+ }
+ if (diskRatio >= 4.0) {
+ rate /= 2;
+ }
+ if (diskRatio >= 6.0) {
+ rate = 0.0;
+ }
+ if (VERBOSE) {
+ System.out.println("\ncycle: " + diskFree + " bytes");
+ }
+ testName = "disk full during reader.close() @ " + thisDiskFree + " bytes";
+ } else {
+ thisDiskFree = 0;
+ rate = 0.0;
+ if (VERBOSE) {
+ System.out.println("\ncycle: same writer: unlimited disk space");
+ }
+ testName = "reader re-use after disk full";
+ }
+
+ dir.setMaxSizeInBytes(thisDiskFree);
+ dir.setRandomIOExceptionRate(rate);
+ Similarity sim = new DefaultSimilarity();
+ try {
+ if (0 == x) {
+ int docId = 12;
+ for(int i=0;i<13;i++) {
+ reader.deleteDocument(docId);
+ reader.setNorm(docId, "content", sim.encodeNormValue(2.0f));
+ docId += 12;
+ }
+ }
+ reader.close();
+ success = true;
+ if (0 == x) {
+ done = true;
+ }
+ } catch (IOException e) {
+ if (VERBOSE) {
+ System.out.println(" hit IOException: " + e);
+ e.printStackTrace(System.out);
+ }
+ err = e;
+ gotExc = true;
+ if (1 == x) {
+ e.printStackTrace();
+ fail(testName + " hit IOException after disk space was freed up");
+ }
+ }
+
+ // Finally, verify index is not corrupt, and, if
+ // we succeeded, we see all docs changed, and if
+ // we failed, we see either all docs or no docs
+ // changed (transactional semantics):
+ IndexReader newReader = null;
+ try {
+ newReader = IndexReader.open(dir, false);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(testName + ":exception when creating IndexReader after disk full during close: " + e);
+ }
+ /*
+ int result = newReader.docFreq(searchTerm);
+ if (success) {
+ if (result != END_COUNT) {
+ fail(testName + ": method did not throw exception but docFreq('aaa') is " + result + " instead of expected " + END_COUNT);
+ }
+ } else {
+ // On hitting exception we still may have added
+ // all docs:
+ if (result != START_COUNT && result != END_COUNT) {
+ err.printStackTrace();
+ fail(testName + ": method did throw exception but docFreq('aaa') is " + result + " instead of expected " + START_COUNT + " or " + END_COUNT);
+ }
+ }
+ */
+
+ IndexSearcher searcher = newSearcher(newReader);
+ ScoreDoc[] hits = null;
+ try {
+ hits = searcher.search(new TermQuery(searchTerm), null, 1000).scoreDocs;
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(testName + ": exception when searching: " + e);
+ }
+ int result2 = hits.length;
+ if (success) {
+ if (result2 != END_COUNT) {
+ fail(testName + ": method did not throw exception but hits.length for search on term 'aaa' is " + result2 + " instead of expected " + END_COUNT);
+ }
+ } else {
+ // On hitting exception we still may have added
+ // all docs:
+ if (result2 != START_COUNT && result2 != END_COUNT) {
+ err.printStackTrace();
+ fail(testName + ": method did throw exception but hits.length for search on term 'aaa' is " + result2 + " instead of expected " + START_COUNT);
+ }
+ }
+
+ searcher.close();
+ newReader.close();
+
+ if (result2 == END_COUNT) {
+ if (!gotExc)
+ fail("never hit disk full");
+ break;
+ }
+ }
+
+ dir.close();
+
+ // Try again with more bytes of free space:
+ diskFree += TEST_NIGHTLY ? _TestUtil.nextInt(random, 5, 20) : _TestUtil.nextInt(random, 50, 200);
+ }
+
+ startDir.close();
+ }
+}