Lucene - AlreadyClosedException: this IndexReader is closed

4.4k views Asked by At

I am new to Lucene, so please bear with me.

I've a class LuceneUtility which my application uses to call RequestIndexSearcher() to acquire indexSearcher object and to perform all search. Every time while return indexSearcher object I'm updating index (if anything needs to be updated) and recreated IndexSearcher object to reflect the new update (if there is any new update) but sometimes I'm getting AlreadyClosedException: this IndexReader is closed.

public class LuceneUtility
{
    private static IndexSearcher _searcher;
    private static Directory _directory;
    private static Lazy<IndexWriter> _writer = new Lazy<IndexWriter>(() => new IndexWriter(_directory, new KeywordLowerCaseAnalyser(), IndexWriter.MaxFieldLength.UNLIMITED));

    private static Object lock_Lucene = new object();

    //this private constructor makes it a singleton now.
    private LuceneUtility() { }

    //Static constructor, opening the directory once for all.
    static LuceneUtility()
    {
        string s ="Path of lucene Index";
        _directory = FSDirectory.Open(s);
    }

    public static IndexSearcher IndexSearcher
    {
        get
        {
            if (_searcher == null)
            {
                InitializeSearcher();
            }
            else if (!_searcher.IndexReader.IsCurrent())
            {

                _searcher.Dispose();
                InitializeSearcher();
            }

            return _searcher;
        }
    }

    public static IndexWriter IndexWriter
    {
        get
        {               
            return _writer.Value;
        }
    }

    private static void InitializeSearcher()
    {
        _searcher = new IndexSearcher(_directory, false);

    }

    public static IndexSearcher RequestIndexSearcher()
    {

        lock (lock_Lucene)
        {
            PerformIndexUpdation();
        }

        return IndexSearcher;
    }
    /// <summary>
    /// Performs Lucene Index Updation
    /// </summary>
    private static void PerformIndexUpdation()
    {

     // Performs Index Updation 
    }

Stacktrace:

     AlreadyClosedException: this IndexReader is closed
    Lucene.Net.Index.IndexReader.EnsureOpen()
   at Lucene.Net.Index.DirectoryReader.IsCurrent()
   at LuceneOperation.LuceneUtility.get_IndexSearcher()
   at LuceneOperation.LuceneUtility.RequestIndexSearcher()

So... What's the deal here...? What am I doing wrong ?

Many thanks in advance! :)

1

There are 1 answers

1
RamblinRose On

The stack trace says it all. Likely a consumer Dispose'd _searcher via a reference returned by IndexSearcher. The following code reproduces the problem (at least one way):

Lucene.Net.Index.IndexWriter sw = LuceneUtility.IndexWriter;
Lucene.Net.Search.IndexSearcher ref1 = LuceneUtility.IndexSearcher;
ref1.Dispose();
// this next call throws at _searcher.IndexReader.IsCurrent()
// because _searcher has been Dispose'd 
Lucene.Net.Search.IndexSearcher ref2 = LuceneUtility.IndexSearcher; 

Worse, IndexSearcher may Dispose an instance of_searcher referenced by a consumer, which could result in the same exception elsewhere:

Lucene.Net.Search.IndexSearcher ref1 = LuceneUtility.IndexSearcher;
// index some documents with the writer
Lucene.Net.Search.IndexSearcher ref2 = LuceneUtility.IndexSearcher;
// acquiring ref2 Dispose'd ref1 because index changed so AlreadyClosedException is thrown
int freq = ref1.DocFreq(new Lucene.Net.Index.Term("text", "RamblinRose"));

Here's a skin-and-bones class that avoids the Dispose issue and the performance pitfall of frequently instantiating IndexSearcher.

public static class MySingletonIndex
{
    private static IndexWriter writer;

    public static void Open(string path)
    {
        if (writer != null)
            throw new Exception("MySingletonIndex is already open");
        // ram directory is a nice option for early stages and experimentation.
        Directory d = path == null ? new RAMDirectory() : (Directory)FSDirectory.Open(path);
        writer = new IndexWriter(d, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.UNLIMITED);
    }

    public static void Close()
    {
        if (writer == null) return;
        writer.Dispose();
        writer = null;
    }
    /// <summary>
    /// Caller must Dispose the IndexSearcher returned.
    /// </summary>
    /// <returns>IndexSearcher</returns>
    public static IndexSearcher GetSearcher()
    {
        if (writer == null)
            throw new Exception("MySingletonIndex is closed");
        return new IndexSearcher(writer.GetReader());           
    }
}

writer.GetReader() is chock full of win.

I'm out of the loop with the latest Lucene.Net offers, so someone experienced with the latest version may offer a better starting point.