From d82624e7f71497f968d7b41f2b81c0e50d006d2f Mon Sep 17 00:00:00 2001 From: Tim Whittington Date: Wed, 26 Jun 2024 21:39:38 +1200 Subject: [PATCH] Fix IndexReader ref leak when Lucene index modified and read in transaction. Change use of MultiReader to take ownership of underlying IndexReader ref counts, and always release IndexReader when Lucene result set is exhausted. Previously, each use of a MultiReader would leak a ref count on the underlying FS SegmentReader, preventing merges in the index and accumulating index files on disk and in in-memory tracking (effectively unbounded if server under continuous load). --- .../lucene/collections/OLuceneResultSet.java | 6 ++---- .../lucene/query/OLuceneQueryContext.java | 11 +++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lucene/src/main/java/com/orientechnologies/lucene/collections/OLuceneResultSet.java b/lucene/src/main/java/com/orientechnologies/lucene/collections/OLuceneResultSet.java index e7b165c283c..6e0b57c52bc 100755 --- a/lucene/src/main/java/com/orientechnologies/lucene/collections/OLuceneResultSet.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/collections/OLuceneResultSet.java @@ -209,10 +209,8 @@ public boolean hasNext() { final boolean hasNext = index < (totalHits - deletedMatchCount); if (!hasNext && !closed) { final IndexSearcher searcher = queryContext.getSearcher(); - if (searcher.getIndexReader().getRefCount() > 1) { - engine.release(searcher); - closed = true; - } + engine.release(searcher); + closed = true; } return hasNext; } diff --git a/lucene/src/main/java/com/orientechnologies/lucene/query/OLuceneQueryContext.java b/lucene/src/main/java/com/orientechnologies/lucene/query/OLuceneQueryContext.java index 4f1b553ec9e..11294c5f247 100755 --- a/lucene/src/main/java/com/orientechnologies/lucene/query/OLuceneQueryContext.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/query/OLuceneQueryContext.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Optional; import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -104,9 +105,15 @@ public IndexSearcher getSearcher() { } private MultiReader multiReader(final OLuceneTxChanges luceneTxChanges) { + final IndexReader primaryReader = searcher.getIndexReader(); + final IndexReader txReader = luceneTxChanges.searcher().getIndexReader(); try { - return new MultiReader( - searcher.getIndexReader(), luceneTxChanges.searcher().getIndexReader()); + // Transfer ownership to the MultiReader so the index searcher can be releases transparently. + // Without this, the primary IndexReader will leak a refcount each time it is wrapped. + MultiReader multiReader = new MultiReader(new IndexReader[] {primaryReader, txReader}, false); + primaryReader.decRef(); + txReader.decRef(); + return multiReader; } catch (final IOException e) { throw OException.wrapException( new OLuceneIndexException("unable to create reader on changes"), e);