1 package org.apache.lucene.index;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
21 import java.io.Reader;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Random;
26 import java.util.concurrent.atomic.AtomicInteger;
27 import java.util.concurrent.atomic.AtomicBoolean;
29 import org.apache.lucene.analysis.Analyzer;
30 import org.apache.lucene.analysis.MockAnalyzer;
31 import org.apache.lucene.analysis.MockTokenizer;
32 import org.apache.lucene.analysis.TokenStream;
33 import org.apache.lucene.document.Document;
34 import org.apache.lucene.document.Field;
35 import org.apache.lucene.search.IndexSearcher;
36 import org.apache.lucene.search.ScoreDoc;
37 import org.apache.lucene.search.TermQuery;
38 import org.apache.lucene.store.Directory;
39 import org.apache.lucene.store.MockDirectoryWrapper;
40 import org.apache.lucene.store.RAMDirectory;
41 import org.apache.lucene.util.LuceneTestCase;
42 import org.apache.lucene.util._TestUtil;
44 public class TestIndexWriterDelete extends LuceneTestCase {
46 // test the simple case
47 public void testSimpleCase() throws IOException {
48 String[] keywords = { "1", "2" };
49 String[] unindexed = { "Netherlands", "Italy" };
50 String[] unstored = { "Amsterdam has lots of bridges",
51 "Venice has lots of canals" };
52 String[] text = { "Amsterdam", "Venice" };
54 Directory dir = newDirectory();
55 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
56 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDeleteTerms(1));
58 for (int i = 0; i < keywords.length; i++) {
59 Document doc = new Document();
60 doc.add(newField("id", keywords[i], Field.Store.YES,
61 Field.Index.NOT_ANALYZED));
62 doc.add(newField("country", unindexed[i], Field.Store.YES,
64 doc.add(newField("contents", unstored[i], Field.Store.NO,
65 Field.Index.ANALYZED));
67 .add(newField("city", text[i], Field.Store.YES,
68 Field.Index.ANALYZED));
69 modifier.addDocument(doc);
71 modifier.forceMerge(1);
74 Term term = new Term("city", "Amsterdam");
75 int hitCount = getHitCount(dir, term);
76 assertEquals(1, hitCount);
77 modifier.deleteDocuments(term);
79 hitCount = getHitCount(dir, term);
80 assertEquals(0, hitCount);
86 // test when delete terms only apply to disk segments
87 public void testNonRAMDelete() throws IOException {
89 Directory dir = newDirectory();
90 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
91 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(2)
92 .setMaxBufferedDeleteTerms(2));
93 modifier.setInfoStream(VERBOSE ? System.out : null);
97 for (int i = 0; i < 7; i++) {
98 addDoc(modifier, ++id, value);
102 assertEquals(0, modifier.getNumBufferedDocuments());
103 assertTrue(0 < modifier.getSegmentCount());
107 IndexReader reader = IndexReader.open(dir, true);
108 assertEquals(7, reader.numDocs());
111 modifier.deleteDocuments(new Term("value", String.valueOf(value)));
115 reader = IndexReader.open(dir, true);
116 assertEquals(0, reader.numDocs());
122 public void testMaxBufferedDeletes() throws IOException {
123 Directory dir = newDirectory();
124 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
125 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDeleteTerms(1));
127 writer.setInfoStream(VERBOSE ? System.out : null);
128 writer.addDocument(new Document());
129 writer.deleteDocuments(new Term("foobar", "1"));
130 writer.deleteDocuments(new Term("foobar", "1"));
131 writer.deleteDocuments(new Term("foobar", "1"));
132 assertEquals(3, writer.getFlushDeletesCount());
137 // test when delete terms only apply to ram segments
138 public void testRAMDeletes() throws IOException {
139 for(int t=0;t<2;t++) {
141 System.out.println("TEST: t=" + t);
143 Directory dir = newDirectory();
144 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
145 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(4)
146 .setMaxBufferedDeleteTerms(4));
147 modifier.setInfoStream(VERBOSE ? System.out : null);
151 addDoc(modifier, ++id, value);
153 modifier.deleteDocuments(new Term("value", String.valueOf(value)));
155 modifier.deleteDocuments(new TermQuery(new Term("value", String.valueOf(value))));
156 addDoc(modifier, ++id, value);
158 modifier.deleteDocuments(new Term("value", String.valueOf(value)));
159 assertEquals(2, modifier.getNumBufferedDeleteTerms());
160 assertEquals(1, modifier.getBufferedDeleteTermsSize());
163 modifier.deleteDocuments(new TermQuery(new Term("value", String.valueOf(value))));
165 addDoc(modifier, ++id, value);
166 assertEquals(0, modifier.getSegmentCount());
169 IndexReader reader = IndexReader.open(dir, true);
170 assertEquals(1, reader.numDocs());
172 int hitCount = getHitCount(dir, new Term("id", String.valueOf(id)));
173 assertEquals(1, hitCount);
180 // test when delete terms apply to both disk and ram segments
181 public void testBothDeletes() throws IOException {
182 Directory dir = newDirectory();
183 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
184 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(100)
185 .setMaxBufferedDeleteTerms(100));
190 for (int i = 0; i < 5; i++) {
191 addDoc(modifier, ++id, value);
195 for (int i = 0; i < 5; i++) {
196 addDoc(modifier, ++id, value);
200 for (int i = 0; i < 5; i++) {
201 addDoc(modifier, ++id, value);
203 modifier.deleteDocuments(new Term("value", String.valueOf(value)));
207 IndexReader reader = IndexReader.open(dir, true);
208 assertEquals(5, reader.numDocs());
214 // test that batched delete terms are flushed together
215 public void testBatchDeletes() throws IOException {
216 Directory dir = newDirectory();
217 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
218 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(2)
219 .setMaxBufferedDeleteTerms(2));
224 for (int i = 0; i < 7; i++) {
225 addDoc(modifier, ++id, value);
229 IndexReader reader = IndexReader.open(dir, true);
230 assertEquals(7, reader.numDocs());
234 modifier.deleteDocuments(new Term("id", String.valueOf(++id)));
235 modifier.deleteDocuments(new Term("id", String.valueOf(++id)));
239 reader = IndexReader.open(dir, true);
240 assertEquals(5, reader.numDocs());
243 Term[] terms = new Term[3];
244 for (int i = 0; i < terms.length; i++) {
245 terms[i] = new Term("id", String.valueOf(++id));
247 modifier.deleteDocuments(terms);
249 reader = IndexReader.open(dir, true);
250 assertEquals(2, reader.numDocs());
258 public void testDeleteAll() throws IOException {
259 Directory dir = newDirectory();
260 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
261 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(2)
262 .setMaxBufferedDeleteTerms(2));
267 for (int i = 0; i < 7; i++) {
268 addDoc(modifier, ++id, value);
272 IndexReader reader = IndexReader.open(dir, true);
273 assertEquals(7, reader.numDocs());
276 // Add 1 doc (so we will have something buffered)
277 addDoc(modifier, 99, value);
280 modifier.deleteAll();
282 // Delete all shouldn't be on disk yet
283 reader = IndexReader.open(dir, true);
284 assertEquals(7, reader.numDocs());
287 // Add a doc and update a doc (after the deleteAll, before the commit)
288 addDoc(modifier, 101, value);
289 updateDoc(modifier, 102, value);
291 // commit the delete all
294 // Validate there are no docs left
295 reader = IndexReader.open(dir, true);
296 assertEquals(2, reader.numDocs());
303 // test rollback of deleteAll()
304 public void testDeleteAllRollback() throws IOException {
305 Directory dir = newDirectory();
306 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
307 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(2)
308 .setMaxBufferedDeleteTerms(2));
313 for (int i = 0; i < 7; i++) {
314 addDoc(modifier, ++id, value);
318 addDoc(modifier, ++id, value);
320 IndexReader reader = IndexReader.open(dir, true);
321 assertEquals(7, reader.numDocs());
325 modifier.deleteAll();
331 // Validate that the docs are still there
332 reader = IndexReader.open(dir, true);
333 assertEquals(7, reader.numDocs());
340 // test deleteAll() w/ near real-time reader
341 public void testDeleteAllNRT() throws IOException {
342 Directory dir = newDirectory();
343 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
344 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDocs(2)
345 .setMaxBufferedDeleteTerms(2));
350 for (int i = 0; i < 7; i++) {
351 addDoc(modifier, ++id, value);
355 IndexReader reader = modifier.getReader();
356 assertEquals(7, reader.numDocs());
359 addDoc(modifier, ++id, value);
360 addDoc(modifier, ++id, value);
363 modifier.deleteAll();
365 reader = modifier.getReader();
366 assertEquals(0, reader.numDocs());
374 // Validate that the docs are still there
375 reader = IndexReader.open(dir, true);
376 assertEquals(7, reader.numDocs());
383 private void updateDoc(IndexWriter modifier, int id, int value)
385 Document doc = new Document();
386 doc.add(newField("content", "aaa", Field.Store.NO, Field.Index.ANALYZED));
387 doc.add(newField("id", String.valueOf(id), Field.Store.YES,
388 Field.Index.NOT_ANALYZED));
389 doc.add(newField("value", String.valueOf(value), Field.Store.NO,
390 Field.Index.NOT_ANALYZED));
391 modifier.updateDocument(new Term("id", String.valueOf(id)), doc);
395 private void addDoc(IndexWriter modifier, int id, int value)
397 Document doc = new Document();
398 doc.add(newField("content", "aaa", Field.Store.NO, Field.Index.ANALYZED));
399 doc.add(newField("id", String.valueOf(id), Field.Store.YES,
400 Field.Index.NOT_ANALYZED));
401 doc.add(newField("value", String.valueOf(value), Field.Store.NO,
402 Field.Index.NOT_ANALYZED));
403 modifier.addDocument(doc);
406 private int getHitCount(Directory dir, Term term) throws IOException {
407 IndexReader reader = IndexReader.open(dir);
408 IndexSearcher searcher = new IndexSearcher(reader);
409 int hitCount = searcher.search(new TermQuery(term), null, 1000).totalHits;
415 public void testDeletesOnDiskFull() throws IOException {
416 doTestOperationsOnDiskFull(false);
419 public void testUpdatesOnDiskFull() throws IOException {
420 doTestOperationsOnDiskFull(true);
424 * Make sure if modifier tries to commit but hits disk full that modifier
425 * remains consistent and usable. Similar to TestIndexReader.testDiskFull().
427 private void doTestOperationsOnDiskFull(boolean updates) throws IOException {
429 Term searchTerm = new Term("content", "aaa");
430 int START_COUNT = 157;
433 // First build up a starting index:
434 MockDirectoryWrapper startDir = newDirectory();
435 // TODO: find the resource leak that only occurs sometimes here.
436 startDir.setNoDeleteOpenFile(false);
437 IndexWriter writer = new IndexWriter(startDir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)));
438 for (int i = 0; i < 157; i++) {
439 Document d = new Document();
440 d.add(newField("id", Integer.toString(i), Field.Store.YES,
441 Field.Index.NOT_ANALYZED));
442 d.add(newField("content", "aaa " + i, Field.Store.NO,
443 Field.Index.ANALYZED));
444 writer.addDocument(d);
448 long diskUsage = startDir.sizeInBytes();
449 long diskFree = diskUsage + 10;
451 IOException err = null;
453 boolean done = false;
455 // Iterate w/ ever increasing free disk space:
458 System.out.println("TEST: cycle");
460 MockDirectoryWrapper dir = new MockDirectoryWrapper(random, new RAMDirectory(startDir));
461 dir.setPreventDoubleWrite(false);
462 IndexWriter modifier = new IndexWriter(dir,
463 newIndexWriterConfig(
464 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false))
465 .setMaxBufferedDocs(1000)
466 .setMaxBufferedDeleteTerms(1000)
467 .setMergeScheduler(new ConcurrentMergeScheduler()));
468 ((ConcurrentMergeScheduler) modifier.getConfig().getMergeScheduler()).setSuppressExceptions();
469 modifier.setInfoStream(VERBOSE ? System.out : null);
471 // For each disk size, first try to commit against
472 // dir that will hit random IOExceptions & disk
473 // full; after, give it infinite disk space & turn
474 // off random IOExceptions & retry w/ same reader:
475 boolean success = false;
477 for (int x = 0; x < 2; x++) {
479 System.out.println("TEST: x=" + x);
483 double diskRatio = ((double)diskFree) / diskUsage;
488 thisDiskFree = diskFree;
489 if (diskRatio >= 2.0) {
492 if (diskRatio >= 4.0) {
495 if (diskRatio >= 6.0) {
499 System.out.println("\ncycle: " + diskFree + " bytes");
501 testName = "disk full during reader.close() @ " + thisDiskFree
507 System.out.println("\ncycle: same writer: unlimited disk space");
509 testName = "reader re-use after disk full";
512 dir.setMaxSizeInBytes(thisDiskFree);
513 dir.setRandomIOExceptionRate(rate);
518 for (int i = 0; i < 13; i++) {
520 Document d = new Document();
521 d.add(newField("id", Integer.toString(i), Field.Store.YES,
522 Field.Index.NOT_ANALYZED));
523 d.add(newField("content", "bbb " + i, Field.Store.NO,
524 Field.Index.ANALYZED));
525 modifier.updateDocument(new Term("id", Integer.toString(docId)), d);
527 modifier.deleteDocuments(new Term("id", Integer.toString(docId)));
528 // modifier.setNorm(docId, "contents", (float)2.0);
539 catch (IOException e) {
541 System.out.println(" hit IOException: " + e);
542 e.printStackTrace(System.out);
547 fail(testName + " hit IOException after disk space was freed up");
552 // Must force the close else the writer can have
553 // open files which cause exc in MockRAMDir.close
557 // If the close() succeeded, make sure there are
558 // no unreferenced files.
560 _TestUtil.checkIndex(dir);
561 TestIndexWriter.assertNoUnreferencedFiles(dir, "after writer.close");
564 // Finally, verify index is not corrupt, and, if
565 // we succeeded, we see all docs changed, and if
566 // we failed, we see either all docs or no docs
567 // changed (transactional semantics):
568 IndexReader newReader = null;
570 newReader = IndexReader.open(dir, true);
572 catch (IOException e) {
575 + ":exception when creating IndexReader after disk full during close: "
579 IndexSearcher searcher = newSearcher(newReader);
580 ScoreDoc[] hits = null;
582 hits = searcher.search(new TermQuery(searchTerm), null, 1000).scoreDocs;
584 catch (IOException e) {
586 fail(testName + ": exception when searching: " + e);
588 int result2 = hits.length;
590 if (x == 0 && result2 != END_COUNT) {
592 + ": method did not throw exception but hits.length for search on term 'aaa' is "
593 + result2 + " instead of expected " + END_COUNT);
594 } else if (x == 1 && result2 != START_COUNT && result2 != END_COUNT) {
595 // It's possible that the first exception was
596 // "recoverable" wrt pending deletes, in which
597 // case the pending deletes are retained and
598 // then re-flushing (with plenty of disk
599 // space) will succeed in flushing the
602 + ": method did not throw exception but hits.length for search on term 'aaa' is "
603 + result2 + " instead of expected " + START_COUNT + " or " + END_COUNT);
606 // On hitting exception we still may have added
608 if (result2 != START_COUNT && result2 != END_COUNT) {
609 err.printStackTrace();
611 + ": method did throw exception but hits.length for search on term 'aaa' is "
612 + result2 + " instead of expected " + START_COUNT + " or " + END_COUNT);
623 // Try again with 10 more bytes of free space:
629 // This test tests that buffered deletes are cleared when
630 // an Exception is hit during flush.
631 public void testErrorAfterApplyDeletes() throws IOException {
633 MockDirectoryWrapper.Failure failure = new MockDirectoryWrapper.Failure() {
634 boolean sawMaybe = false;
635 boolean failed = false;
638 public MockDirectoryWrapper.Failure reset() {
639 thread = Thread.currentThread();
645 public void eval(MockDirectoryWrapper dir) throws IOException {
646 if (Thread.currentThread() != thread) {
647 // don't fail during merging
650 if (sawMaybe && !failed) {
651 boolean seen = false;
652 StackTraceElement[] trace = new Exception().getStackTrace();
653 for (int i = 0; i < trace.length; i++) {
654 if ("applyDeletes".equals(trace[i].getMethodName())) {
660 // Only fail once we are no longer in applyDeletes
663 System.out.println("TEST: mock failure: now fail");
664 new Throwable().printStackTrace(System.out);
666 throw new IOException("fail after applyDeletes");
670 StackTraceElement[] trace = new Exception().getStackTrace();
671 for (int i = 0; i < trace.length; i++) {
672 if ("applyDeletes".equals(trace[i].getMethodName())) {
674 System.out.println("TEST: mock failure: saw applyDeletes");
675 new Throwable().printStackTrace(System.out);
685 // create a couple of files
687 String[] keywords = { "1", "2" };
688 String[] unindexed = { "Netherlands", "Italy" };
689 String[] unstored = { "Amsterdam has lots of bridges",
690 "Venice has lots of canals" };
691 String[] text = { "Amsterdam", "Venice" };
693 MockDirectoryWrapper dir = newDirectory();
694 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig(
695 TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).setMaxBufferedDeleteTerms(2).setReaderPooling(false).setMergePolicy(newLogMergePolicy()));
696 modifier.setInfoStream(VERBOSE ? System.out : null);
698 LogMergePolicy lmp = (LogMergePolicy) modifier.getConfig().getMergePolicy();
699 lmp.setUseCompoundFile(true);
701 dir.failOn(failure.reset());
703 for (int i = 0; i < keywords.length; i++) {
704 Document doc = new Document();
705 doc.add(newField("id", keywords[i], Field.Store.YES,
706 Field.Index.NOT_ANALYZED));
707 doc.add(newField("country", unindexed[i], Field.Store.YES,
709 doc.add(newField("contents", unstored[i], Field.Store.NO,
710 Field.Index.ANALYZED));
711 doc.add(newField("city", text[i], Field.Store.YES,
712 Field.Index.ANALYZED));
713 modifier.addDocument(doc);
715 // flush (and commit if ac)
718 System.out.println("TEST: now full merge");
721 modifier.forceMerge(1);
723 System.out.println("TEST: now commit");
727 // one of the two files hits
729 Term term = new Term("city", "Amsterdam");
730 int hitCount = getHitCount(dir, term);
731 assertEquals(1, hitCount);
733 // open the writer again (closed above)
736 // max buf del terms is two, so this is buffered
739 System.out.println("TEST: delete term=" + term);
742 modifier.deleteDocuments(term);
744 // add a doc (needed for the !ac case; see below)
745 // doc remains buffered
748 System.out.println("TEST: add empty doc");
750 Document doc = new Document();
751 modifier.addDocument(doc);
753 // commit the changes, the buffered deletes, and the new doc
755 // The failure object will fail on the first write after the del
756 // file gets created when processing the buffered delete
758 // in the ac case, this will be when writing the new segments
759 // files so we really don't need the new doc, but it's harmless
761 // a new segments file won't be created but in this
762 // case, creation of the cfs file happens next so we
763 // need the doc (to test that it's okay that we don't
764 // lose deletes if failing while creating the cfs file)
765 boolean failed = false;
768 System.out.println("TEST: now commit for failure");
771 } catch (IOException ioe) {
778 // The commit above failed, so we need to retry it (which will
779 // succeed, because the failure is a one-shot)
783 hitCount = getHitCount(dir, term);
785 // Make sure the delete was successfully flushed:
786 assertEquals(0, hitCount);
792 // This test tests that the files created by the docs writer before
793 // a segment is written are cleaned up if there's an i/o error
795 public void testErrorInDocsWriterAdd() throws IOException {
797 MockDirectoryWrapper.Failure failure = new MockDirectoryWrapper.Failure() {
798 boolean failed = false;
800 public MockDirectoryWrapper.Failure reset() {
805 public void eval(MockDirectoryWrapper dir) throws IOException {
808 throw new IOException("fail in add doc");
813 // create a couple of files
815 String[] keywords = { "1", "2" };
816 String[] unindexed = { "Netherlands", "Italy" };
817 String[] unstored = { "Amsterdam has lots of bridges",
818 "Venice has lots of canals" };
819 String[] text = { "Amsterdam", "Venice" };
821 MockDirectoryWrapper dir = newDirectory();
822 IndexWriter modifier = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)));
824 dir.failOn(failure.reset());
826 for (int i = 0; i < keywords.length; i++) {
827 Document doc = new Document();
828 doc.add(newField("id", keywords[i], Field.Store.YES,
829 Field.Index.NOT_ANALYZED));
830 doc.add(newField("country", unindexed[i], Field.Store.YES,
832 doc.add(newField("contents", unstored[i], Field.Store.NO,
833 Field.Index.ANALYZED));
834 doc.add(newField("city", text[i], Field.Store.YES,
835 Field.Index.ANALYZED));
837 modifier.addDocument(doc);
838 } catch (IOException io) {
840 System.out.println("TEST: got expected exc:");
841 io.printStackTrace(System.out);
848 TestIndexWriter.assertNoUnreferencedFiles(dir, "docswriter abort() failed to delete unreferenced files");
852 private String arrayToString(String[] l) {
854 for (int i = 0; i < l.length; i++) {
863 public void testDeleteAllSlowly() throws Exception {
864 final Directory dir = newDirectory();
865 RandomIndexWriter w = new RandomIndexWriter(random, dir);
866 final int NUM_DOCS = atLeast(1000);
867 final List<Integer> ids = new ArrayList<Integer>(NUM_DOCS);
868 for(int id=0;id<NUM_DOCS;id++) {
871 Collections.shuffle(ids, random);
873 Document doc = new Document();
874 doc.add(newField("id", ""+id, Field.Index.NOT_ANALYZED));
877 Collections.shuffle(ids, random);
879 while(upto < ids.size()) {
880 final int left = ids.size() - upto;
881 final int inc = Math.min(left, _TestUtil.nextInt(random, 1, 20));
882 final int limit = upto + inc;
883 while(upto < limit) {
884 w.deleteDocuments(new Term("id", ""+ids.get(upto++)));
886 final IndexReader r = w.getReader();
887 assertEquals(NUM_DOCS - upto, r.numDocs());
895 public void testIndexingThenDeleting() throws Exception {
896 final Random r = random;
897 Directory dir = newDirectory();
898 // note this test explicitly disables payloads
899 final Analyzer analyzer = new Analyzer() {
901 public TokenStream tokenStream(String fieldName, Reader reader) {
902 return new MockTokenizer(reader, MockTokenizer.WHITESPACE, true);
905 IndexWriter w = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, analyzer).setRAMBufferSizeMB(1.0).setMaxBufferedDocs(IndexWriterConfig.DISABLE_AUTO_FLUSH).setMaxBufferedDeleteTerms(IndexWriterConfig.DISABLE_AUTO_FLUSH));
906 w.setInfoStream(VERBOSE ? System.out : null);
907 Document doc = new Document();
908 doc.add(newField("field", "go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20", Field.Store.NO, Field.Index.ANALYZED));
909 int num = atLeast(3);
910 for (int iter = 0; iter < num; iter++) {
913 final boolean doIndexing = r.nextBoolean();
915 System.out.println("TEST: iter doIndexing=" + doIndexing);
918 // Add docs until a flush is triggered
919 final int startFlushCount = w.getFlushCount();
920 while(w.getFlushCount() == startFlushCount) {
925 // Delete docs until a flush is triggered
926 final int startFlushCount = w.getFlushCount();
927 while(w.getFlushCount() == startFlushCount) {
928 w.deleteDocuments(new Term("foo", ""+count));
932 assertTrue("flush happened too quickly during " + (doIndexing ? "indexing" : "deleting") + " count=" + count, count > 3000);
938 // LUCENE-3340: make sure deletes that we don't apply
939 // during flush (ie are just pushed into the stream) are
940 // in fact later flushed due to their RAM usage:
941 public void testFlushPushedDeletesByRAM() throws Exception {
942 Directory dir = newDirectory();
943 // Cannot use RandomIndexWriter because we don't want to
944 // ever call commit() for this test:
945 // note: tiny rambuffer used, as with a 1MB buffer the test is too slow (flush @ 128,999)
946 IndexWriter w = new IndexWriter(dir,
947 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random))
948 .setRAMBufferSizeMB(0.2f).setMaxBufferedDocs(1000).setMergePolicy(NoMergePolicy.NO_COMPOUND_FILES).setReaderPooling(false));
949 w.setInfoStream(VERBOSE ? System.out : null);
952 Document doc = new Document();
953 doc.add(new Field("id", count+"", Field.Store.NO, Field.Index.NOT_ANALYZED));
956 // This is the only delete that applies
957 delTerm = new Term("id", ""+0);
959 // These get buffered, taking up RAM, but delete
960 // nothing when applied:
961 delTerm = new Term("id", "x" + count);
963 w.updateDocument(delTerm, doc);
964 // Eventually segment 0 should get a del docs:
965 if (dir.fileExists("_0_1.del")) {
967 System.out.println("TEST: deletes created @ count=" + count);
973 // Today we applyDeletes @ count=21553; even if we make
974 // sizable improvements to RAM efficiency of buffered
975 // del term we're unlikely to go over 100K:
976 if (count > 100000) {
977 fail("delete's were not applied");
984 // LUCENE-3340: make sure deletes that we don't apply
985 // during flush (ie are just pushed into the stream) are
986 // in fact later flushed due to their RAM usage:
987 public void testFlushPushedDeletesByCount() throws Exception {
988 Directory dir = newDirectory();
989 // Cannot use RandomIndexWriter because we don't want to
990 // ever call commit() for this test:
991 final int flushAtDelCount = atLeast(1020);
992 IndexWriter w = new IndexWriter(dir,
993 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
994 setMaxBufferedDeleteTerms(flushAtDelCount).setMaxBufferedDocs(1000).setRAMBufferSizeMB(IndexWriterConfig.DISABLE_AUTO_FLUSH).setMergePolicy(NoMergePolicy.NO_COMPOUND_FILES).setReaderPooling(false));
995 w.setInfoStream(VERBOSE ? System.out : null);
997 System.out.println("TEST: flush @ " + flushAtDelCount + " buffered delete terms");
1001 Document doc = new Document();
1002 doc.add(new Field("id", count+"", Field.Store.NO, Field.Index.NOT_ANALYZED));
1004 if (count == 1010) {
1005 // This is the only delete that applies
1006 delTerm = new Term("id", ""+0);
1008 // These get buffered, taking up RAM, but delete
1009 // nothing when applied:
1010 delTerm = new Term("id", "x" + count);
1012 w.updateDocument(delTerm, doc);
1013 // Eventually segment 0 should get a del docs:
1014 if (dir.fileExists("_0_1.del")) {
1018 if (count > flushAtDelCount) {
1019 fail("delete's were not applied at count=" + flushAtDelCount);
1026 // Make sure buffered (pushed) deletes don't use up so
1027 // much RAM that it forces long tail of tiny segments:
1028 public void testApplyDeletesOnFlush() throws Exception {
1029 Directory dir = newDirectory();
1030 // Cannot use RandomIndexWriter because we don't want to
1031 // ever call commit() for this test:
1032 final AtomicInteger docsInSegment = new AtomicInteger();
1033 final AtomicBoolean closing = new AtomicBoolean();
1034 final AtomicBoolean sawAfterFlush = new AtomicBoolean();
1035 IndexWriter w = new IndexWriter(dir,
1036 newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1037 setRAMBufferSizeMB(0.5).setMaxBufferedDocs(-1).setMergePolicy(NoMergePolicy.NO_COMPOUND_FILES).setReaderPooling(false)) {
1039 public void doAfterFlush() {
1040 assertTrue("only " + docsInSegment.get() + " in segment", closing.get() || docsInSegment.get() >= 7);
1041 docsInSegment.set(0);
1042 sawAfterFlush.set(true);
1045 w.setInfoStream(VERBOSE ? System.out : null);
1048 StringBuilder sb = new StringBuilder();
1049 for(int termIDX=0;termIDX<100;termIDX++) {
1050 sb.append(' ').append(_TestUtil.randomRealisticUnicodeString(random));
1053 w.deleteDocuments(new Term("id", "0"));
1055 Document doc = new Document();
1056 doc.add(newField("id", ""+id, Field.Index.NOT_ANALYZED));
1057 doc.add(newField("body", sb.toString(), Field.Index.ANALYZED));
1058 w.updateDocument(new Term("id", ""+id), doc);
1059 docsInSegment.incrementAndGet();
1060 if (dir.fileExists("_0_1.del")) {
1062 System.out.println("TEST: deletes created @ id=" + id);
1069 assertTrue(sawAfterFlush.get());