This is a patch for Bug 12588.

Problem:

If a reader is opened (reader1) on an index without acquiring a write.lock and
later the index is changed by either another reader (reader2) or a writer, the
first reader can still acquire a write.lock and change the index. Thus
the changes by reader1 or the writer are lost. My patch adds an additional check
to Indexreader.delete(int) which checks whether the index has changed since the reader
was opened. Since this check is done with a valid write.lock no other reader/writer
could interfere with the check. If the index has been changed the reader is not
granted write permission. The only way to delete a document in such a case is to
get a new reader and call delete there. The patch for IndexReader and a unit test are
attached.

Christoph

--
*****************************************************************
* Dr. Christoph Goller       Tel.:   +49 89 203 45734           *
* Detego Software GmbH       Mobile: +49 179 1128469            *
* Keuslinstr. 13             Fax.:   +49 721 151516176          *
* 80798 München, Germany     Email:  [EMAIL PROTECTED]  *
*****************************************************************
Index: IndexReader.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-lucene/src/java/org/apache/lucene/index/IndexReader.java,v
retrieving revision 1.17
diff -u -r1.17 IndexReader.java
--- IndexReader.java    12 Aug 2003 15:05:03 -0000      1.17
+++ IndexReader.java    12 Sep 2003 11:28:14 -0000
@@ -80,10 +80,14 @@
 public abstract class IndexReader {
   protected IndexReader(Directory directory) {
     this.directory = directory;
+    segmentInfosAge = Long.MAX_VALUE;
   }
 
   Directory directory;
   private Lock writeLock;
+  
+  //used to determine whether index has chaged since reader was opened
+  private long segmentInfosAge;
 
   /** Returns an IndexReader reading the index in an FSDirectory in the named
   path. */
@@ -102,15 +106,20 @@
     synchronized (directory) {                   // in- & inter-process sync
       return (IndexReader)new Lock.With(directory.makeLock("commit.lock"), 
IndexWriter.COMMIT_LOCK_TIMEOUT) {
          public Object doBody() throws IOException {
+            IndexReader result = null;
+            
            SegmentInfos infos = new SegmentInfos();
            infos.read(directory);
            if (infos.size() == 1)                // index is optimized
-             return new SegmentReader(infos.info(0), true);
+             result = new SegmentReader(infos.info(0), true);
 
            SegmentReader[] readers = new SegmentReader[infos.size()];
            for (int i = 0; i < infos.size(); i++)
              readers[i] = new SegmentReader(infos.info(i), i==infos.size()-1);
-           return new SegmentsReader(directory, readers);
+           result =  new SegmentsReader(directory, readers);
+        
+            result.segmentInfosAge = directory.fileModified("segments");
+            return result;
          }
        }.run();
     }
@@ -258,6 +267,14 @@
       if (!writeLock.obtain(IndexWriter.WRITE_LOCK_TIMEOUT)) // obtain write lock
         throw new IOException("Index locked for write: " + writeLock);
       this.writeLock = writeLock;
+      
+      // we have to check whether index has changed since this reader was opened.
+      // if so, this reader is no longer valid for deletion
+      if(lastModified(directory) > segmentInfosAge){
+          this.writeLock.release();
+          this.writeLock = null;
+          throw new IOException("IndexReader out of date and no longer valid for 
deletion");
+      }
     }
     doDelete(docNum);
   }
/*
 * Created on 11.09.2003
 *
 * To change the template for this generated file go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
package org.apache.lucene.index;

import java.io.IOException;

import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;

import junit.framework.TestCase;

/**
 * 
 * @author goller
 */
public class TestDelete extends TestCase {
    
    public void testDeleteReaderWriterConflict(){
        
        Directory dir = new RAMDirectory();
        IndexWriter writer = null;
        IndexReader reader = null;
        Searcher searcher = null;
        Term searchTerm = new Term("content", "aaa");
        Hits hits = null;
        
        try {
            
            //  add 100 documents with term : aaa
           writer  = new IndexWriter(dir, new WhitespaceAnalyzer(), true);
           for (int i = 0; i < 100; i++) {
             addDoc(writer, "aaa");
           }
           writer.close();
           
           reader = IndexReader.open(dir);
           
           //  add 100 documents with term : bbb
          writer  = new IndexWriter(dir, new WhitespaceAnalyzer(), false);
          for (int i = 0; i < 100; i++) {
            addDoc(writer, "bbb");
          }
          writer.optimize();
          writer.close();
          
        }
        catch (IOException e) {
          e.printStackTrace();
        }
           
        try{
            // delete documents containing term: aaa
            reader.delete(searchTerm);
            reader.close();
        }
        catch (IOException e) {
            try {
                // if reader throws IOException try once more to delete documents with 
a new reader
                reader.close();
                reader = IndexReader.open(dir);
                reader.delete(searchTerm);
                reader.close();
            }
            catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        try {
            searcher = new IndexSearcher(dir);
            hits = searcher.search(new TermQuery(searchTerm));
            assertEquals(0, hits.length());
            searcher.close();
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
          
        
        
    }
    
    public void testDeleteReaderReaderConflict(){
        
        Directory dir = new RAMDirectory();
        IndexWriter writer = null;
        IndexReader reader1 = null;
        IndexReader reader2 = null;
        Searcher searcher = null;
        Hits hits = null;
        Term searchTerm1 = new Term("content", "aaa");
        Term searchTerm2 = new Term("content", "bbb");
        
        try {
            
            //  add 100 documents with term : aaa
            //  add 100 documents with term : bbb
            //  add 100 documents with term : ccc
           writer  = new IndexWriter(dir, new WhitespaceAnalyzer(), true);
           for (int i = 0; i < 100; i++) {
             addDoc(writer, "aaa");
             addDoc(writer, "bbb");
             addDoc(writer, "ccc");
           }
          writer.optimize();
          writer.close();
          
        }
        catch (IOException e) {
          e.printStackTrace();
        }
        
           
        try{
            
            reader1 = IndexReader.open(dir);
            reader2 = IndexReader.open(dir);
            
            // delete documents containing term: aaa
            reader2.delete(searchTerm1);
            reader2.close();
            
            // delete documents containing term: bbb
            reader1.delete(searchTerm2);
            reader1.close();
        }
        catch (IOException e) {
            try {
                // if reader throws IOException try once more to delete documents with 
a new reader
                reader1.close();
                reader1 = IndexReader.open(dir);
                reader1.delete(searchTerm2);
                reader1.close();
            }
            catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        try {
            searcher = new IndexSearcher(dir);
            hits = searcher.search(new TermQuery(searchTerm1));
            assertEquals(0, hits.length());
            hits = searcher.search(new TermQuery(searchTerm2));
            assertEquals(0, hits.length());
            searcher.close();
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
        
    }
    
    
    private void addDoc(IndexWriter writer, String value)
    {
      Document doc = new Document();
      doc.add(Field.UnStored("content", value));

      try {
        writer.addDocument(doc);
      }
      catch (IOException e) {
        e.printStackTrace();
      }
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to