Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 1042904) +++ CHANGES.txt (working copy) @@ -131,6 +131,11 @@ to determine whether the passed in segment should be compound. (Shai Erera, Earwin Burrfoot) +* LUCENE-2805: IndexWriter now increments the index version on every change to + the index instead of for every commit. Committing or closing the IndexWriter + without any changes to the index will not cause any index version increment. + (Simon Willnauer, Mike Mccandless) + API Changes * LUCENE-2302, LUCENE-1458, LUCENE-2111, LUCENE-2514: Terms are no longer Index: contrib/misc/src/java/org/apache/lucene/index/IndexSplitter.java =================================================================== --- contrib/misc/src/java/org/apache/lucene/index/IndexSplitter.java (revision 1042904) +++ contrib/misc/src/java/org/apache/lucene/index/IndexSplitter.java (working copy) @@ -134,6 +134,7 @@ int idx = getIdx(n); infos.remove(idx); } + infos.changed(); infos.commit(fsDir); } @@ -152,6 +153,7 @@ copyFile(srcFile, destFile); } } + destInfos.changed(); destInfos.commit(destFSDir); // System.out.println("destDir:"+destDir.getAbsolutePath()); } Index: src/java/org/apache/lucene/index/CheckIndex.java =================================================================== --- src/java/org/apache/lucene/index/CheckIndex.java (revision 1042904) +++ src/java/org/apache/lucene/index/CheckIndex.java (working copy) @@ -879,6 +879,7 @@ public void fixIndex(Status result) throws IOException { if (result.partial) throw new IllegalArgumentException("can only fix an index that was fully checked (this status checked a subset of segments)"); + result.newSegments.changed(); result.newSegments.commit(result.dir); } Index: src/java/org/apache/lucene/index/DirectoryReader.java =================================================================== --- src/java/org/apache/lucene/index/DirectoryReader.java (revision 1042904) +++ src/java/org/apache/lucene/index/DirectoryReader.java (working copy) @@ -770,6 +770,7 @@ deletionPolicy == null ? new KeepOnlyLastCommitDeletionPolicy() : deletionPolicy, segmentInfos, null, null, codecs); segmentInfos.updateGeneration(deleter.getLastSegmentInfos()); + segmentInfos.changed(); // Checkpoint the state we are about to change, in // case we have to roll back: @@ -782,7 +783,6 @@ // Sync all files we just wrote directory.sync(segmentInfos.files(directory, false)); - segmentInfos.commit(directory); success = true; } finally { Index: src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- src/java/org/apache/lucene/index/IndexWriter.java (revision 1042904) +++ src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -741,6 +741,7 @@ // Record that we have a change (zero out all // segments) pending: changeCount++; + segmentInfos.changed(); } else { segmentInfos.read(directory, codecs); @@ -757,6 +758,7 @@ oldInfos.read(directory, commit.getSegmentsFileName(), codecs); segmentInfos.replace(oldInfos); changeCount++; + segmentInfos.changed(); if (infoStream != null) message("init: loaded commit \"" + commit.getSegmentsFileName() + "\""); } @@ -774,12 +776,14 @@ conf.getIndexDeletionPolicy(), segmentInfos, infoStream, docWriter, codecs); - if (deleter.startingCommitDeleted) + if (deleter.startingCommitDeleted) { // Deletion policy deleted the "head" commit point. // We have to mark ourself as changed so that if we // are closed w/o any further changes we write a new // segments_N file. changeCount++; + segmentInfos.changed(); + } docWriter.setMaxBufferedDeleteTerms(conf.getMaxBufferedDeleteTerms()); docWriter.setRAMBufferSizeMB(conf.getRAMBufferSizeMB()); @@ -1537,6 +1541,7 @@ // name that was previously returned which can cause // problems at least with ConcurrentMergeScheduler. changeCount++; + segmentInfos.changed(); return "_" + Integer.toString(segmentInfos.counter++, Character.MAX_RADIX); } } @@ -2038,6 +2043,7 @@ // Mark that the index has changed ++changeCount; + segmentInfos.changed(); } catch (OutOfMemoryError oom) { handleOOM(oom, "deleteAll"); } finally { @@ -2119,6 +2125,7 @@ */ private synchronized void checkpoint() throws IOException { changeCount++; + segmentInfos.changed(); deleter.checkpoint(segmentInfos, false); } @@ -3686,6 +3693,7 @@ } toSync.remove(toSync.size()-1); changeCount++; + segmentInfos.changed(); } } assert filesExist(toSync); @@ -3818,17 +3826,7 @@ } synchronized boolean nrtIsCurrent(SegmentInfos infos) { - if (!infos.equals(segmentInfos)) { - // if any structural changes (new segments), we are - // stale - return false; - } else if (infos.getGeneration() != segmentInfos.getGeneration()) { - // if any commit took place since we were opened, we - // are stale - return false; - } else { - return !docWriter.anyChanges(); - } + return infos.version == segmentInfos.version && !docWriter.anyChanges(); } synchronized boolean isClosed() { Index: src/java/org/apache/lucene/index/SegmentInfos.java =================================================================== --- src/java/org/apache/lucene/index/SegmentInfos.java (revision 1042904) +++ src/java/org/apache/lucene/index/SegmentInfos.java (working copy) @@ -696,7 +696,6 @@ void updateGeneration(SegmentInfos other) { lastGeneration = other.lastGeneration; generation = other.generation; - version = other.version; } final void rollbackCommit(Directory dir) throws IOException { @@ -727,7 +726,12 @@ * segments file, but writes an invalid checksum at the * end, so that it is not visible to readers. Once this * is called you must call {@link #finishCommit} to complete - * the commit or {@link #rollbackCommit} to abort it. */ + * the commit or {@link #rollbackCommit} to abort it. + *

+ * Note: {@link #changed()} should be called prior to this + * method if changes have been made to this {@link SegmentInfos} instance + *

+ **/ final void prepareCommit(Directory dir) throws IOException { if (pendingSegnOutput != null) throw new IllegalStateException("prepareCommit was already called"); @@ -811,7 +815,12 @@ } /** Writes & syncs to the Directory dir, taking care to - * remove the segments file on exception */ + * remove the segments file on exception + *

+ * Note: {@link #changed()} should be called prior to this + * method if changes have been made to this {@link SegmentInfos} instance + *

+ **/ final void commit(Directory dir) throws IOException { prepareCommit(dir); finishCommit(dir); @@ -862,4 +871,10 @@ } return count; } + + /** Call this before committing if changes have been made to the + * segments. */ + public void changed() { + version++; + } } Index: src/java/org/apache/lucene/index/codecs/DefaultSegmentInfosWriter.java =================================================================== --- src/java/org/apache/lucene/index/codecs/DefaultSegmentInfosWriter.java (revision 1042904) +++ src/java/org/apache/lucene/index/codecs/DefaultSegmentInfosWriter.java (working copy) @@ -51,8 +51,7 @@ throws IOException { IndexOutput out = createOutput(dir, segmentFileName); out.writeInt(FORMAT_CURRENT); // write FORMAT - out.writeLong(++infos.version); // every write changes - // the index + out.writeLong(infos.version); out.writeInt(infos.counter); // write counter out.writeInt(infos.size()); // write infos for (SegmentInfo si : infos) { Index: src/test/org/apache/lucene/index/TestIndexWriterReader.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexWriterReader.java (revision 1042904) +++ src/test/org/apache/lucene/index/TestIndexWriterReader.java (working copy) @@ -61,7 +61,54 @@ } return count; } - + + public void testAddCloseOpen() throws IOException { + Directory dir1 = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer()); + + IndexWriter writer = new IndexWriter(dir1, iwc); + for (int i = 0; i < 97 ; i++) { + IndexReader reader = writer.getReader(); + if (i == 0) { + writer.addDocument(createDocument(i, "x", 1 + random.nextInt(5))); + } else { + int previous = random.nextInt(i); + // a check if the reader is current here could fail since there might be + // merges going on. + switch (random.nextInt(5)) { + case 0: + case 1: + case 2: + writer.addDocument(createDocument(i, "x", 1 + random.nextInt(5))); + break; + case 3: + writer.updateDocument(new Term("id", "" + previous), createDocument( + previous, "x", 1 + random.nextInt(5))); + break; + case 4: + writer.deleteDocuments(new Term("id", "" + previous)); + } + } + assertFalse(reader.isCurrent()); + reader.close(); + } + writer.optimize(); // make sure all merging is done etc. + IndexReader reader = writer.getReader(); + writer.commit(); // no changes that are not visible to the reader + assertTrue(reader.isCurrent()); + writer.close(); + assertTrue(reader.isCurrent()); // all changes are visible to the reader + iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer()); + writer = new IndexWriter(dir1, iwc); + assertTrue(reader.isCurrent()); + writer.addDocument(createDocument(1, "x", 1+random.nextInt(5))); + assertTrue(reader.isCurrent()); // segments in ram but IW is different to the readers one + writer.close(); + assertFalse(reader.isCurrent()); // segments written + reader.close(); + dir1.close(); + } + public void testUpdateDocument() throws Exception { boolean optimize = true; @@ -171,7 +218,7 @@ assertTrue(r1.isCurrent()); writer.commit(); - assertFalse(r1.isCurrent()); + assertTrue(r1.isCurrent()); // we have seen all changes - no change after opening the NRT reader assertEquals(200, r1.maxDoc());