blob: 20b12667abb02b6c50271fa936401782249fc06d [file] [log] [blame]
/*
* 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.
*/
package org.apache.lucene.index;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FilterDirectory;
import org.apache.lucene.store.MockDirectoryWrapper;
import org.apache.lucene.store.NoLockFactory;
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.TestUtil;
import org.apache.lucene.util.TimeUnits;
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
@SuppressCodecs({ "SimpleText", "Direct" })
@TimeoutSuite(millis = 8 * TimeUnits.HOUR)
public class TestIndexWriterMaxDocs extends LuceneTestCase {
// The two hour time was achieved on a Linux 3.13 system with these specs:
// 3-core AMD at 2.5Ghz, 12 GB RAM, 5GB test heap, 2 test JVMs, 2TB SATA.
@Monster("takes over two hours")
public void testExactlyAtTrueLimit() throws Exception {
Directory dir = newFSDirectory(createTempDir("2BDocs3"));
IndexWriter iw = new IndexWriter(dir, new IndexWriterConfig(null));
Document doc = new Document();
doc.add(newStringField("field", "text", Field.Store.NO));
for (int i = 0; i < IndexWriter.MAX_DOCS; i++) {
iw.addDocument(doc);
/*
if (i%1000000 == 0) {
System.out.println((i/1000000) + " M docs...");
}
*/
}
iw.commit();
// First unoptimized, then optimized:
for(int i=0;i<2;i++) {
DirectoryReader ir = DirectoryReader.open(dir);
assertEquals(IndexWriter.MAX_DOCS, ir.maxDoc());
assertEquals(IndexWriter.MAX_DOCS, ir.numDocs());
IndexSearcher searcher = new IndexSearcher(ir);
TopScoreDocCollector collector = TopScoreDocCollector.create(10, Integer.MAX_VALUE);
searcher.search(new TermQuery(new Term("field", "text")), collector);
TopDocs hits = collector.topDocs();
assertEquals(IndexWriter.MAX_DOCS, hits.totalHits.value);
// Sort by docID reversed:
hits = searcher.search(new TermQuery(new Term("field", "text")), 10, new Sort(new SortField(null, SortField.Type.DOC, true)));
assertEquals(IndexWriter.MAX_DOCS, hits.totalHits.value);
assertEquals(10, hits.scoreDocs.length);
assertEquals(IndexWriter.MAX_DOCS-1, hits.scoreDocs[0].doc);
ir.close();
iw.forceMerge(1);
}
iw.close();
dir.close();
}
public void testAddDocument() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
public void testAddDocuments() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.addDocuments(Collections.singletonList(new Document()));
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
public void testUpdateDocument() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.updateDocument(new Term("field", "foo"), new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
public void testUpdateDocuments() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.updateDocuments(new Term("field", "foo"), Collections.singletonList(new Document()));
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
public void testReclaimedDeletes() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
Document doc = new Document();
doc.add(newStringField("id", ""+i, Field.Store.NO));
w.addDocument(doc);
}
// Delete 5 of them:
for(int i=0;i<5;i++) {
w.deleteDocuments(new Term("id", ""+i));
}
w.forceMerge(1);
assertEquals(5, w.getDocStats().maxDoc);
// Add 5 more docs
for(int i=0;i<5;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// Tests that 100% deleted segments (which IW "specializes" by dropping entirely) are not mis-counted
public void testReclaimedDeletesWholeSegments() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriterConfig iwc = new IndexWriterConfig(null);
iwc.setMergePolicy(NoMergePolicy.INSTANCE);
IndexWriter w = new IndexWriter(dir, iwc);
for(int i=0;i<10;i++) {
Document doc = new Document();
doc.add(newStringField("id", ""+i, Field.Store.NO));
w.addDocument(doc);
if (i % 2 == 0) {
// Make a new segment every 2 docs:
w.commit();
}
}
// Delete 5 of them:
for(int i=0;i<5;i++) {
w.deleteDocuments(new Term("id", ""+i));
}
w.forceMerge(1);
assertEquals(5, w.getDocStats().maxDoc);
// Add 5 more docs
for(int i=0;i<5;i++) {
w.addDocument(new Document());
}
// 11th document should fail:
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
public void testAddIndexes() throws Exception {
setIndexWriterMaxDocs(10);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for(int i=0;i<10;i++) {
w.addDocument(new Document());
}
w.close();
Directory dir2 = newDirectory();
IndexWriter w2 = new IndexWriter(dir2, new IndexWriterConfig(null));
w2.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w2.addIndexes(new Directory[] {dir});
});
assertEquals(1, w2.getDocStats().maxDoc);
DirectoryReader ir = DirectoryReader.open(dir);
expectThrows(IllegalArgumentException.class, () -> {
TestUtil.addIndexesSlowly(w2, ir);
});
w2.close();
ir.close();
dir.close();
dir2.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// Make sure MultiReader lets you search exactly the limit number of docs:
public void testMultiReaderExactLimit() throws Exception {
Directory dir = newDirectory();
Document doc = new Document();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for (int i = 0; i < 100000; i++) {
w.addDocument(doc);
}
w.close();
int remainder = IndexWriter.MAX_DOCS % 100000;
Directory dir2 = newDirectory();
w = new IndexWriter(dir2, new IndexWriterConfig(null));
for (int i = 0; i < remainder; i++) {
w.addDocument(doc);
}
w.close();
int copies = IndexWriter.MAX_DOCS / 100000;
DirectoryReader ir = DirectoryReader.open(dir);
DirectoryReader ir2 = DirectoryReader.open(dir2);
IndexReader subReaders[] = new IndexReader[copies+1];
Arrays.fill(subReaders, ir);
subReaders[subReaders.length-1] = ir2;
MultiReader mr = new MultiReader(subReaders);
assertEquals(IndexWriter.MAX_DOCS, mr.maxDoc());
assertEquals(IndexWriter.MAX_DOCS, mr.numDocs());
ir.close();
ir2.close();
dir.close();
dir2.close();
}
// Make sure MultiReader is upset if you exceed the limit
public void testMultiReaderBeyondLimit() throws Exception {
Directory dir = newDirectory();
Document doc = new Document();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for (int i = 0; i < 100000; i++) {
w.addDocument(doc);
}
w.close();
int remainder = IndexWriter.MAX_DOCS % 100000;
// One too many:
remainder++;
Directory dir2 = newDirectory();
w = new IndexWriter(dir2, new IndexWriterConfig(null));
for (int i = 0; i < remainder; i++) {
w.addDocument(doc);
}
w.close();
int copies = IndexWriter.MAX_DOCS / 100000;
DirectoryReader ir = DirectoryReader.open(dir);
DirectoryReader ir2 = DirectoryReader.open(dir2);
IndexReader subReaders[] = new IndexReader[copies+1];
Arrays.fill(subReaders, ir);
subReaders[subReaders.length-1] = ir2;
expectThrows(IllegalArgumentException.class, () -> {
new MultiReader(subReaders);
});
ir.close();
ir2.close();
dir.close();
dir2.close();
}
/**
* LUCENE-6299: Test if addindexes(Dir[]) prevents exceeding max docs.
*/
// TODO: can we use the setter to lower the amount of docs to be written here?
@Nightly
public void testAddTooManyIndexesDir() throws Exception {
// we cheat and add the same one over again... IW wants a write lock on each
Directory dir = newDirectory(random(), NoLockFactory.INSTANCE);
Document doc = new Document();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for (int i = 0; i < 100000; i++) {
w.addDocument(doc);
}
w.forceMerge(1);
w.commit();
w.close();
// wrap this with disk full, so test fails faster and doesn't fill up real disks.
MockDirectoryWrapper dir2 = newMockDirectory();
w = new IndexWriter(dir2, new IndexWriterConfig(null));
w.commit(); // don't confuse checkindex
dir2.setMaxSizeInBytes(dir2.sizeInBytes() + 65536); // 64KB
Directory dirs[] = new Directory[1 + (IndexWriter.MAX_DOCS / 100000)];
for (int i = 0; i < dirs.length; i++) {
// bypass iw check for duplicate dirs
dirs[i] = new FilterDirectory(dir) {};
}
try {
w.addIndexes(dirs);
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
// pass
} catch (IOException fakeDiskFull) {
final Exception e;
if (fakeDiskFull.getMessage() != null && fakeDiskFull.getMessage().startsWith("fake disk full")) {
e = new RuntimeException("test failed: IW checks aren't working and we are executing addIndexes");
e.addSuppressed(fakeDiskFull);
} else {
e = fakeDiskFull;
}
throw e;
}
w.close();
dir.close();
dir2.close();
}
/**
* LUCENE-6299: Test if addindexes(CodecReader[]) prevents exceeding max docs.
*/
public void testAddTooManyIndexesCodecReader() throws Exception {
// we cheat and add the same one over again... IW wants a write lock on each
Directory dir = newDirectory(random(), NoLockFactory.INSTANCE);
Document doc = new Document();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
for (int i = 0; i < 100000; i++) {
w.addDocument(doc);
}
w.forceMerge(1);
w.commit();
w.close();
// wrap this with disk full, so test fails faster and doesn't fill up real disks.
MockDirectoryWrapper dir2 = newMockDirectory();
w = new IndexWriter(dir2, new IndexWriterConfig(null));
w.commit(); // don't confuse checkindex
dir2.setMaxSizeInBytes(dir2.sizeInBytes() + 65536); // 64KB
IndexReader r = DirectoryReader.open(dir);
CodecReader segReader = (CodecReader) r.leaves().get(0).reader();
CodecReader readers[] = new CodecReader[1 + (IndexWriter.MAX_DOCS / 100000)];
for (int i = 0; i < readers.length; i++) {
readers[i] = segReader;
}
try {
w.addIndexes(readers);
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
// pass
} catch (IOException fakeDiskFull) {
final Exception e;
if (fakeDiskFull.getMessage() != null && fakeDiskFull.getMessage().startsWith("fake disk full")) {
e = new RuntimeException("test failed: IW checks aren't working and we are executing addIndexes");
e.addSuppressed(fakeDiskFull);
} else {
e = fakeDiskFull;
}
throw e;
}
r.close();
w.close();
dir.close();
dir2.close();
}
public void testTooLargeMaxDocs() {
expectThrows(IllegalArgumentException.class, () -> {
IndexWriter.setMaxDocs(Integer.MAX_VALUE);
});
}
// LUCENE-6299
public void testDeleteAll() throws Exception {
setIndexWriterMaxDocs(1);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.deleteAll();
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testDeleteAllAfterFlush() throws Exception {
setIndexWriterMaxDocs(2);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.getReader().close();
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.deleteAll();
w.addDocument(new Document());
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testDeleteAllAfterCommit() throws Exception {
setIndexWriterMaxDocs(2);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.commit();
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.deleteAll();
w.addDocument(new Document());
w.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testDeleteAllMultipleThreads() throws Exception {
int limit = TestUtil.nextInt(random(), 2, 10);
setIndexWriterMaxDocs(limit);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
CountDownLatch startingGun = new CountDownLatch(1);
Thread[] threads = new Thread[limit];
for(int i=0;i<limit;i++) {
threads[i] = new Thread() {
@Override
public void run() {
try {
startingGun.await();
w.addDocument(new Document());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
threads[i].start();
}
startingGun.countDown();
for(Thread thread : threads) {
thread.join();
}
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.deleteAll();
for(int i=0;i<limit;i++) {
w.addDocument(new Document());
}
expectThrows(IllegalArgumentException.class, () -> {
w.addDocument(new Document());
});
w.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testDeleteAllAfterClose() throws Exception {
setIndexWriterMaxDocs(2);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.close();
IndexWriter w2 = new IndexWriter(dir, new IndexWriterConfig(null));
w2.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w2.addDocument(new Document());
});
w2.deleteAll();
w2.addDocument(new Document());
w2.addDocument(new Document());
expectThrows(IllegalArgumentException.class, () -> {
w2.addDocument(new Document());
});
w2.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testAcrossTwoIndexWriters() throws Exception {
setIndexWriterMaxDocs(1);
try {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.close();
IndexWriter w2 = new IndexWriter(dir, new IndexWriterConfig(null));
expectThrows(IllegalArgumentException.class, () -> {
w2.addDocument(new Document());
});
w2.close();
dir.close();
} finally {
restoreIndexWriterMaxDocs();
}
}
// LUCENE-6299
public void testCorruptIndexExceptionTooLarge() throws Exception {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.addDocument(new Document());
w.close();
setIndexWriterMaxDocs(1);
try {
expectThrows(CorruptIndexException.class, () -> {
DirectoryReader.open(dir);
});
} finally {
restoreIndexWriterMaxDocs();
}
dir.close();
}
// LUCENE-6299
public void testCorruptIndexExceptionTooLargeWriter() throws Exception {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));
w.addDocument(new Document());
w.addDocument(new Document());
w.close();
setIndexWriterMaxDocs(1);
try {
expectThrows(CorruptIndexException.class, () -> {
new IndexWriter(dir, new IndexWriterConfig(null));
});
} finally {
restoreIndexWriterMaxDocs();
}
dir.close();
}
}