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]