Author: chetanm
Date: Thu Aug  3 06:28:06 2017
New Revision: 1803955

URL: http://svn.apache.org/viewvc?rev=1803955&view=rev
Log:
OAK-6500 - NRTIndex leaks file handles due to unclosed IndexReader

Actual fix by using ref counting for IndexReader instances

Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java?rev=1803955&r1=1803954&r2=1803955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 Thu Aug  3 06:28:06 2017
@@ -22,6 +22,7 @@ import static com.google.common.base.Pre
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -86,7 +87,7 @@ public class IndexNodeManager {
 
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
 
-    private volatile IndexSearcher indexSearcher;
+    private volatile SearcherHolder searcherHolder;
 
     private final NRTIndex nrtIndex;
 
@@ -101,8 +102,6 @@ public class IndexNodeManager {
 
     private boolean closed = false;
 
-    private List<LuceneIndexReader> nrtReaders;
-
     private final int indexNodeId = INDEX_NODE_COUNTER.incrementAndGet();
 
     IndexNodeManager(String name, IndexDefinition definition, 
List<LuceneIndexReader> readers, @Nullable NRTIndex nrtIndex)
@@ -112,8 +111,7 @@ public class IndexNodeManager {
         this.definition = definition;
         this.readers = readers;
         this.nrtIndex = nrtIndex;
-        this.nrtReaders = getNRTReaders();
-        this.indexSearcher = new IndexSearcher(createReader(nrtReaders));
+        this.searcherHolder = createHolder(getNRTReaders());
         this.refreshPolicy = nrtIndex != null ? nrtIndex.getRefreshPolicy() : 
ReaderRefreshPolicy.NEVER;
     }
 
@@ -135,6 +133,7 @@ public class IndexNodeManager {
         return readers.isEmpty() ? null : getDefaultReader().getLookup();
     }
 
+    @CheckForNull
     IndexNode acquire() {
         lock.readLock().lock();
         if (closed) {
@@ -145,7 +144,7 @@ public class IndexNodeManager {
             try {
                 refreshPolicy.refreshOnReadIfRequired(refreshCallback);
                 success = true;
-                return new IndexNodeImpl(indexSearcher);
+                return new IndexNodeImpl(searcherHolder);
             } finally {
                 if (!success) {
                     lock.readLock().unlock();
@@ -171,12 +170,8 @@ public class IndexNodeManager {
             lock.writeLock().unlock();
         }
 
-        //Do not close the NRTIndex here as it might be in use
-        //by newer IndexNode. Just close the readers obtained from
-        //them
-        for (LuceneIndexReader reader : Iterables.concat(readers, nrtReaders)){
-           reader.close();
-        }
+        releaseHolder(searcherHolder);
+        closeReaders(readers);
     }
 
     private List<LuceneIndexReader> getPrimaryReaders() {
@@ -197,9 +192,10 @@ public class IndexNodeManager {
         List<LuceneIndexReader> newNRTReaders = getNRTReaders();
         //The list reference would differ if index got updated
         //so if they are same no need to reinitialize the searcher
-        if (newNRTReaders != nrtReaders) {
-            nrtReaders = newNRTReaders;
-            indexSearcher = new IndexSearcher(createReader(nrtReaders));
+        if (newNRTReaders != searcherHolder.nrtReaders) {
+            SearcherHolder old = searcherHolder;
+            searcherHolder = createHolder(newNRTReaders);
+            releaseHolder(old);
             PERF_LOGGER.end(start, 0, "Refreshed reader for index [{}]", 
definition);
         }
     }
@@ -210,39 +206,96 @@ public class IndexNodeManager {
     }
 
     private IndexReader createReader(List<LuceneIndexReader> nrtReaders) {
+        //Increment count by 1. MultiReader does it for all readers
+        //So no need for an explicit increment for MultiReader
+
         if (readers.size() == 1 && nrtReaders.isEmpty()){
-            return readers.get(0).getReader();
+            IndexReader reader = readers.get(0).getReader();
+            reader.incRef();
+            return reader;
         }
         if (nrtReaders.size() == 1 && readers.isEmpty()){
-            return nrtReaders.get(0).getReader();
+            IndexReader reader = nrtReaders.get(0).getReader();
+            reader.incRef();
+            return reader;
         }
+
         IndexReader[] readerArr = new IndexReader[readers.size() + 
nrtReaders.size()];
         int i = 0;
         for (LuceneIndexReader r : Iterables.concat(readers, nrtReaders)){
             readerArr[i++] = r.getReader();
         }
-        return new MultiReader(readerArr, true);
+        return new MultiReader(readerArr, false);
     }
 
     private List<LuceneIndexReader> getNRTReaders() {
         return nrtIndex != null ? nrtIndex.getReaders() : 
Collections.<LuceneIndexReader>emptyList();
     }
 
-    private class IndexNodeImpl implements IndexNode {
-        private final IndexSearcher searcher;
+    private SearcherHolder createHolder(List<LuceneIndexReader> newNRTReaders) 
{
+        return new SearcherHolder(new 
IndexSearcher(createReader(newNRTReaders)), newNRTReaders);
+    }
+
+    private void closeReaders(Iterable<LuceneIndexReader> readers) {
+        for (LuceneIndexReader r : readers){
+            try {
+                r.close();
+            } catch (IOException e) {
+                log.warn("Error occurred while releasing reader for index 
[{}]", definition.getIndexPath(), e);
+            }
+        }
+    }
+
+    private void releaseHolder(SearcherHolder holder) {
+        decrementSearcherUsageCount(holder.searcher);
+    }
 
-        private IndexNodeImpl(IndexSearcher searcher) {
+    private static void incrementSearcherUsageCount(IndexSearcher searcher) {
+        searcher.getIndexReader().incRef();
+    }
+
+    private void decrementSearcherUsageCount(IndexSearcher searcher) {
+        try {
+            //Decrement the count by 1 as we increased it while creating the 
searcher
+            //in createReader
+            searcher.getIndexReader().decRef();
+        } catch (IOException e) {
+            log.warn("Error occurred while releasing reader for index [{}]", 
definition.getIndexPath(), e);
+        }
+    }
+
+    private static class SearcherHolder {
+        final IndexSearcher searcher;
+        final List<LuceneIndexReader> nrtReaders;
+
+        public SearcherHolder(IndexSearcher searcher, List<LuceneIndexReader> 
nrtReaders) {
             this.searcher = searcher;
+            this.nrtReaders = nrtReaders;
+        }
+    }
+
+    private class IndexNodeImpl implements IndexNode {
+        private final SearcherHolder holder;
+        private final AtomicBoolean released = new AtomicBoolean();
+
+        private IndexNodeImpl(SearcherHolder searcherHolder) {
+            this.holder = searcherHolder;
+            //Increment on each acquire
+            incrementSearcherUsageCount(holder.searcher);
         }
 
         @Override
         public void release() {
-            IndexNodeManager.this.release();
+            if (released.compareAndSet(false, true)) {
+                //Decrement on each release
+                decrementSearcherUsageCount(holder.searcher);
+                IndexNodeManager.this.release();
+            }
         }
 
         @Override
         public IndexSearcher getSearcher() {
-            return searcher;
+            return holder.searcher;
         }
 
         @Override
@@ -262,7 +315,7 @@ public class IndexNodeManager {
 
         @Override
         public List<LuceneIndexReader> getNRTReaders() {
-            return IndexNodeManager.this.nrtReaders;
+            return holder.nrtReaders;
         }
 
         @Override

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java?rev=1803955&r1=1803954&r2=1803955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
 Thu Aug  3 06:28:06 2017
@@ -110,8 +110,12 @@ public class NRTIndex implements Closeab
 
     @CheckForNull
     LuceneIndexReader getPrimaryReader() {
-        DirectoryReader reader = createReader();
-        return reader != null ? new NRTReader(reader, directory) : null;
+        DirectoryReader latestReader = createReader();
+        if (latestReader != dirReader) {
+            decrementReaderUseCount(dirReader);
+            dirReader = latestReader;
+        }
+        return latestReader != null ? new NRTReader(latestReader, directory) : 
null;
     }
 
     public LuceneIndexWriter getWriter() throws IOException {
@@ -145,6 +149,9 @@ public class NRTIndex implements Closeab
         if (previousReader != null) {
             newReaders.add(previousReader);
         }
+
+        decrementReaderUseCount(dirReader);
+
         dirReader = latestReader;
         readers = ImmutableList.copyOf(newReaders);
         return readers;
@@ -161,6 +168,10 @@ public class NRTIndex implements Closeab
 
         log.debug("[{}] Closing NRTIndex [{}]", definition.getIndexPath(), 
getName());
 
+        if (dirReader != null){
+            dirReader.close();
+        }
+
         assertAllReadersAreClosed();
 
         if (indexWriter != null) {
@@ -212,6 +223,17 @@ public class NRTIndex implements Closeab
         }
     }
 
+    private void decrementReaderUseCount(IndexReader reader) {
+        try {
+            if (reader != null) {
+                reader.decRef();
+            }
+        } catch (IOException e) {
+            log.warn("[{}] Error occurred while releasing reader instance {}",
+                    definition.getIndexPath(), toString(), e);
+        }
+    }
+
     /**
      * If index was updated then a new reader would be returned otherwise
      * existing reader would be returned

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java?rev=1803955&r1=1803954&r2=1803955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
 Thu Aug  3 06:28:06 2017
@@ -138,8 +138,7 @@ public class HybridIndexTest extends Abs
         MountInfoProvider mip = defaultMountInfoProvider();
 
         nrtIndexFactory = new NRTIndexFactory(copier, clock, 
TimeUnit.MILLISECONDS.toSeconds(refreshDelta), StatisticsProvider.NOOP);
-        //TODO OAK-6500
-        //nrtIndexFactory.setAssertAllResourcesClosed(true);
+        nrtIndexFactory.setAssertAllResourcesClosed(true);
         LuceneIndexReaderFactory indexReaderFactory = new 
DefaultIndexReaderFactory(mip, copier);
         IndexTracker tracker = new 
IndexTracker(indexReaderFactory,nrtIndexFactory);
         luceneIndexProvider = new LuceneIndexProvider(tracker);
@@ -351,7 +350,6 @@ public class HybridIndexTest extends Abs
         assertQuery(query, of("/b", "/c"));
     }
 
-    @Ignore("OAK-6500")
     @Test
     public void noFileLeaks() throws Exception{
         nrtIndexFactory.setDirectoryFactory(new NRTDirectoryFactory() {

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java?rev=1803955&r1=1803954&r2=1803955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
 Thu Aug  3 06:28:06 2017
@@ -66,7 +66,7 @@ public class NRTIndexTest {
     public void setUp() throws IOException {
         indexCopier = new IndexCopier(sameThreadExecutor(), 
temporaryFolder.getRoot());
         indexFactory = new NRTIndexFactory(indexCopier, 
StatisticsProvider.NOOP);
-        //indexFactory.setAssertAllResourcesClosed(true);
+        indexFactory.setAssertAllResourcesClosed(true);
         LuceneIndexEditorContext.configureUniqueId(builder);
     }
 


Reply via email to