Author: chetanm
Date: Thu Aug 10 11:13:05 2017
New Revision: 1804664

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

Merge 1803247,1803248,1803249,1803951,1803953,1803954,1803955

Added:
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
      - copied, changed from r1803951, 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTDirectoryFactory.java
      - copied unchanged from r1803247, 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTDirectoryFactory.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java
      - copied, changed from r1803951, 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java
Removed:
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeTest.java
Modified:
    jackrabbit/oak/branches/1.6/   (props changed)
    jackrabbit/oak/branches/1.6/oak-lucene/pom.xml
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactory.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactoryTest.java
    
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java

Propchange: jackrabbit/oak/branches/1.6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Aug 10 11:13:05 2017
@@ -1,3 +1,3 @@
 /jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1781068,1781075,1781248,1781386,1781846,1781907,1782000,1782029,1782196,1782447,1782476,1782770,1782945,1782966,1782973,1782990,1783061,1783066,1783089,1783104-1783105,1783110,1783619,1783720,1783731,1783733,1783738,1783742,1783773,1783855,1783891,1784023,1784034,1784130,1784162,1784251,1784401,1784551,1784574,1784689,1785095,1785108,1785283,1785838,1785917,1785919,1785946,1786122,1787074,1787145,1787217,1787425,1788056,1788378,1788387-1788389,1788850,1789056,1789534,1790382,1792463,1792742,1792746,1793013,1793088,1793618,1793627,1793644,1795138,1795314,1795330,1795475,1795488,1795491,1795502,1795594,1795613,1795618,1796144,1796230,1796239,1796274,1796278,1796988,1798035,1798834,1799219,1799389,1799393,1799924,1800269,1800606,1800613,1800974,1801011,1801013,1801118-1801119,1801675,1802260,1802262,1802286,1802548,1802934,1802938,1802973,1803026
+/jackrabbit/oak/trunk:1781068,1781075,1781248,1781386,1781846,1781907,1782000,1782029,1782196,1782447,1782476,1782770,1782945,1782966,1782973,1782990,1783061,1783066,1783089,1783104-1783105,1783110,1783619,1783720,1783731,1783733,1783738,1783742,1783773,1783855,1783891,1784023,1784034,1784130,1784162,1784251,1784401,1784551,1784574,1784689,1785095,1785108,1785283,1785838,1785917,1785919,1785946,1786122,1787074,1787145,1787217,1787425,1788056,1788378,1788387-1788389,1788850,1789056,1789534,1790382,1792463,1792742,1792746,1793013,1793088,1793618,1793627,1793644,1795138,1795314,1795330,1795475,1795488,1795491,1795502,1795594,1795613,1795618,1796144,1796230,1796239,1796274,1796278,1796988,1798035,1798834,1799219,1799389,1799393,1799924,1800269,1800606,1800613,1800974,1801011,1801013,1801118-1801119,1801675,1802260,1802262,1802286,1802548,1802934,1802938,1802973,1803026,1803247-1803249,1803951,1803953-1803955
 /jackrabbit/trunk:1345480

Modified: jackrabbit/oak/branches/1.6/oak-lucene/pom.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/pom.xml?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-lucene/pom.xml (original)
+++ jackrabbit/oak/branches/1.6/oak-lucene/pom.xml Thu Aug 10 11:13:05 2017
@@ -343,5 +343,11 @@
       <version>1.10.19</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-exec</artifactId>
+      <version>1.3</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
 Thu Aug 10 11:13:05 2017
@@ -1,237 +1,57 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
-package org.apache.jackrabbit.oak.plugins.index.lucene;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
+package org.apache.jackrabbit.oak.plugins.index.lucene;
 
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
 
 import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
 
-import com.google.common.collect.Iterables;
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
-import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.NRTIndex;
-import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.NRTIndexFactory;
-import 
org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.ReaderRefreshPolicy;
 import org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReader;
-import 
org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReaderFactory;
 import org.apache.jackrabbit.oak.plugins.index.lucene.writer.LuceneIndexWriter;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.util.PerfLogger;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.MultiReader;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.suggest.analyzing.AnalyzingInfixSuggester;
 import org.apache.lucene.store.Directory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class IndexNode {
-    /**
-     * Name of the hidden node under which information about the checkpoints
-     * seen and indexed by each async indexer is kept.
-     */
-    static final String ASYNC = ":async";
-
-    private static final AtomicInteger INDEX_NODE_COUNTER = new 
AtomicInteger();
-
-    private static final PerfLogger PERF_LOGGER =
-            new PerfLogger(LoggerFactory.getLogger(IndexNode.class.getName() + 
".perf"));
-
-    static IndexNode open(String indexPath, NodeState root, NodeState 
defnNodeState,
-                          LuceneIndexReaderFactory readerFactory, @Nullable 
NRTIndexFactory nrtFactory)
-            throws IOException {
-        IndexDefinition definition = new IndexDefinition(root, defnNodeState, 
indexPath);
-        List<LuceneIndexReader> readers = 
readerFactory.createReaders(definition, defnNodeState, indexPath);
-        NRTIndex nrtIndex = nrtFactory != null ? 
nrtFactory.createIndex(definition) : null;
-        if (!readers.isEmpty() || (nrtIndex != null && 
!hasAsyncIndexerRun(root))){
-            return new IndexNode(PathUtils.getName(indexPath), definition, 
readers, nrtIndex);
-        }
-        return null;
-    }
-
-    static boolean hasAsyncIndexerRun(NodeState root) {
-        return root.hasChildNode(ASYNC);
-    }
-
-    private static final Logger log = LoggerFactory.getLogger(IndexNode.class);
-
-    private final List<LuceneIndexReader> readers;
 
-    private final String name;
+public interface IndexNode {
 
-    private final IndexDefinition definition;
+    void release();
 
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    IndexSearcher getSearcher();
 
-    private volatile IndexSearcher indexSearcher;
+    IndexDefinition getDefinition();
 
-    private final NRTIndex nrtIndex;
+    List<LuceneIndexReader> getPrimaryReaders();
 
-    private final ReaderRefreshPolicy refreshPolicy;
-
-    private final Runnable refreshCallback = new Runnable() {
-        @Override
-        public void run() {
-            refreshReaders();
-        }
-    };
-
-    private boolean closed = false;
-
-    private List<LuceneIndexReader> nrtReaders;
-
-    private final int indexNodeId = INDEX_NODE_COUNTER.incrementAndGet();
-
-    IndexNode(String name, IndexDefinition definition, List<LuceneIndexReader> 
readers, @Nullable NRTIndex nrtIndex)
-            throws IOException {
-        checkArgument(!readers.isEmpty() || nrtIndex != null);
-        this.name = name;
-        this.definition = definition;
-        this.readers = readers;
-        this.nrtIndex = nrtIndex;
-        this.nrtReaders = getNRTReaders();
-        this.indexSearcher = new IndexSearcher(createReader(nrtReaders));
-        this.refreshPolicy = nrtIndex != null ? nrtIndex.getRefreshPolicy() : 
ReaderRefreshPolicy.NEVER;
-    }
-
-    String getName() {
-        return name;
-    }
-
-    IndexDefinition getDefinition() {
-        return definition;
-    }
+    @CheckForNull
+    Directory getSuggestDirectory();
 
-    public IndexSearcher getSearcher() {
-        return indexSearcher;
-    }
+    List<LuceneIndexReader> getNRTReaders();
 
     @CheckForNull
-    Directory getSuggestDirectory() {
-        return readers.isEmpty() ? null : 
getDefaultReader().getSuggestDirectory();
-    }
+    AnalyzingInfixSuggester getLookup();
 
-    @CheckForNull
-    AnalyzingInfixSuggester getLookup() {
-        return readers.isEmpty() ? null : getDefaultReader().getLookup();
-    }
-
-    boolean acquire() {
-        lock.readLock().lock();
-        if (closed) {
-            lock.readLock().unlock();
-            return false;
-        } else {
-            boolean success = false;
-            try {
-                refreshPolicy.refreshOnReadIfRequired(refreshCallback);
-                success = true;
-                return true;
-            } finally {
-                if (!success) {
-                    lock.readLock().unlock();
-                }
-            }
-        }
-    }
-
-    public void release() {
-        lock.readLock().unlock();
-    }
-
-    public int getIndexNodeId() {
-        return indexNodeId;
-    }
-
-    void close() throws IOException {
-        lock.writeLock().lock();
-        try {
-            checkState(!closed);
-            closed = true;
-        } finally {
-            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();
-        }
-    }
-
-    List<LuceneIndexReader> getPrimaryReaders() {
-        return readers;
-    }
+    int getIndexNodeId();
 
     @CheckForNull
-    public LuceneIndexWriter getLocalWriter() throws IOException{
-        return nrtIndex != null ? nrtIndex.getWriter() : null;
-    }
-
-    public void refreshReadersOnWriteIfRequired() {
-        refreshPolicy.refreshOnWriteIfRequired(refreshCallback);
-    }
-
-    private void refreshReaders(){
-        long start = PERF_LOGGER.start();
-        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));
-            PERF_LOGGER.end(start, 0, "Refreshed reader for index [{}]", 
definition);
-        }
-    }
-
-    private LuceneIndexReader getDefaultReader(){
-        //TODO This is still required to support Suggester, Spellcheck etc 
OAK-4643
-        return readers.get(0);
-    }
-
-    private IndexReader createReader(List<LuceneIndexReader> nrtReaders) {
-        if (readers.size() == 1 && nrtReaders.isEmpty()){
-            return readers.get(0).getReader();
-        }
-        if (nrtReaders.size() == 1 && readers.isEmpty()){
-            return nrtReaders.get(0).getReader();
-        }
-        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);
-    }
-
-    List<LuceneIndexReader> getNRTReaders() {
-        return nrtIndex != null ? nrtIndex.getReaders() : 
Collections.<LuceneIndexReader>emptyList();
-    }
-
+    LuceneIndexWriter getLocalWriter() throws IOException;
 
+    void refreshReadersOnWriteIfRequired();
 }

Copied: 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 (from r1803951, 
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/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java?p2=jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java&p1=jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java&r1=1803951&r2=1804664&rev=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 Thu Aug 10 11:13:05 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;
@@ -39,7 +40,7 @@ import org.apache.jackrabbit.oak.plugins
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReaderFactory;
 import org.apache.jackrabbit.oak.plugins.index.lucene.writer.LuceneIndexWriter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.commons.benchmark.PerfLogger;
+import org.apache.jackrabbit.oak.util.PerfLogger;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiReader;
 import org.apache.lucene.search.IndexSearcher;
@@ -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/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
 Thu Aug 10 11:13:05 2017
@@ -16,7 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
-import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.in;
 import static com.google.common.base.Predicates.not;
 import static com.google.common.base.Predicates.notNull;
@@ -69,7 +69,7 @@ public class IndexTracker {
 
     private NodeState root = EMPTY_NODE;
 
-    private volatile Map<String, IndexNode> indices = emptyMap();
+    private volatile Map<String, IndexNodeManager> indices = emptyMap();
 
     private volatile boolean refresh;
 
@@ -91,10 +91,10 @@ public class IndexTracker {
     }
 
     synchronized void close() {
-        Map<String, IndexNode> indices = this.indices;
+        Map<String, IndexNodeManager> indices = this.indices;
         this.indices = emptyMap();
 
-        for (Map.Entry<String, IndexNode> entry : indices.entrySet()) {
+        for (Map.Entry<String, IndexNodeManager> entry : indices.entrySet()) {
             try {
                 entry.getValue().close();
             } catch (IOException e) {
@@ -115,8 +115,8 @@ public class IndexTracker {
     }
 
     private synchronized void diffAndUpdate(final NodeState root) {
-        Map<String, IndexNode> original = indices;
-        final Map<String, IndexNode> updates = newHashMap();
+        Map<String, IndexNodeManager> original = indices;
+        final Map<String, IndexNodeManager> updates = newHashMap();
 
         Set<String> indexPaths = Sets.newHashSet();
         indexPaths.addAll(original.keySet());
@@ -129,7 +129,7 @@ public class IndexTracker {
                 public void leave(NodeState before, NodeState after) {
                     try {
                         long start = PERF_LOGGER.start();
-                        IndexNode index = IndexNode.open(path, root, after, 
readerFactory, nrtFactory);
+                        IndexNodeManager index = IndexNodeManager.open(path, 
root, after, readerFactory, nrtFactory);
                         PERF_LOGGER.end(start, -1, "[{}] Index found to be 
updated. Reopening the IndexNode", path);
                         updates.put(path, index); // index can be null
                     } catch (IOException e) {
@@ -143,7 +143,7 @@ public class IndexTracker {
         this.root = root;
 
         if (!updates.isEmpty()) {
-            indices = ImmutableMap.<String, IndexNode>builder()
+            indices = ImmutableMap.<String, IndexNodeManager>builder()
                     .putAll(filterKeys(original, not(in(updates.keySet()))))
                     .putAll(filterValues(updates, notNull()))
                     .build();
@@ -155,7 +155,7 @@ public class IndexTracker {
             //Given that Tracker is now invoked from a BackgroundObserver
             //not a high concern
             for (String path : updates.keySet()) {
-                IndexNode index = original.get(path);
+                IndexNodeManager index = original.get(path);
                 try {
                     if (index != null) {
                         index.close();
@@ -172,9 +172,10 @@ public class IndexTracker {
     }
 
     public IndexNode acquireIndexNode(String path) {
-        IndexNode index = indices.get(path);
-        if (index != null && index.acquire()) {
-            return index;
+        IndexNodeManager index = indices.get(path);
+        IndexNode indexNode = index != null ? index.acquire() : null;
+        if (indexNode != null) {
+            return indexNode;
         } else {
             return findIndexNode(path);
         }
@@ -182,7 +183,7 @@ public class IndexTracker {
 
     @CheckForNull
     public IndexDefinition getIndexDefinition(String indexPath){
-        IndexNode node = indices.get(indexPath);
+        IndexNodeManager node = indices.get(indexPath);
         if (node != null){
             //Accessing the definition should not require
             //locking as its immutable state
@@ -207,10 +208,10 @@ public class IndexTracker {
         // Retry the lookup from acquireIndexNode now that we're
         // synchronized. The acquire() call is guaranteed to succeed
         // since the close() method is also synchronized.
-        IndexNode index = indices.get(path);
+        IndexNodeManager index = indices.get(path);
         if (index != null) {
-            checkState(index.acquire());
-            return index;
+            IndexNode indexNode = index.acquire();
+            return checkNotNull(indexNode);
         }
 
         if (badIndexTracker.isIgnoredBadIndex(path)){
@@ -224,15 +225,16 @@ public class IndexTracker {
 
         try {
             if (isLuceneIndexNode(node)) {
-                index = IndexNode.open(path, root, node, readerFactory, 
nrtFactory);
+                index = IndexNodeManager.open(path, root, node, readerFactory, 
nrtFactory);
                 if (index != null) {
-                    checkState(index.acquire());
-                    indices = ImmutableMap.<String, IndexNode>builder()
+                    IndexNode indexNode = index.acquire();
+                    checkNotNull(indexNode);
+                    indices = ImmutableMap.<String, IndexNodeManager>builder()
                             .putAll(indices)
                             .put(path, index)
                             .build();
                     badIndexTracker.markGoodIndex(path);
-                    return index;
+                    return indexNode;
                 }
             } else if (node.exists()) {
                 log.warn("Cannot open Lucene Index at path {} as the index is 
not of type {}", path, TYPE_LUCENE);

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndex.java
 Thu Aug 10 11:13:05 2017
@@ -22,6 +22,8 @@ package org.apache.jackrabbit.oak.plugin
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -48,8 +50,6 @@ import org.apache.lucene.index.IndexWrit
 import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.search.suggest.analyzing.AnalyzingInfixSuggester;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.store.NRTCachingDirectory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -75,6 +75,7 @@ public class NRTIndex implements Closeab
     private final TimerStats refreshTimer;
     private final HistogramStats sizeHisto;
     private final TimerStats.Context openTime;
+    private final NRTDirectoryFactory directoryFactory;
 
     private NRTIndex previous;
 
@@ -85,15 +86,22 @@ public class NRTIndex implements Closeab
     private DirectoryReader dirReader;
     private boolean closed;
     private List<LuceneIndexReader> readers;
+    private final List<IndexReader> openedReaders;
+    private final boolean assertAllReadersClosed;
+
 
     public NRTIndex(IndexDefinition definition, IndexCopier indexCopier,
                     IndexUpdateListener refreshPolicy, @Nullable NRTIndex 
previous,
-                    StatisticsProvider statisticsProvider) {
+                    StatisticsProvider statisticsProvider, NRTDirectoryFactory 
directoryFactory,
+                    boolean assertAllReadersClosed) {
         this.definition = definition;
         this.indexCopier = indexCopier;
         this.refreshPolicy = refreshPolicy;
         this.previous = previous;
         this.statisticsProvider = statisticsProvider;
+        this.directoryFactory = directoryFactory;
+        this.assertAllReadersClosed = assertAllReadersClosed;
+        this.openedReaders = assertAllReadersClosed ? new 
LinkedList<IndexReader>() : Collections.<IndexReader>emptyList();
 
         this.refreshTimer = 
statisticsProvider.getTimer(metricName("REFRESH_TIME"), 
StatsOptions.METRICS_ONLY);
         this.sizeHisto = statisticsProvider.getHistogram(metricName("SIZE"), 
StatsOptions.METRICS_ONLY);
@@ -102,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 {
@@ -137,6 +149,9 @@ public class NRTIndex implements Closeab
         if (previousReader != null) {
             newReaders.add(previousReader);
         }
+
+        decrementReaderUseCount(dirReader);
+
         dirReader = latestReader;
         readers = ImmutableList.copyOf(newReaders);
         return readers;
@@ -150,6 +165,15 @@ public class NRTIndex implements Closeab
         if (closed) {
             return;
         }
+
+        log.debug("[{}] Closing NRTIndex [{}]", definition.getIndexPath(), 
getName());
+
+        if (dirReader != null){
+            dirReader.close();
+        }
+
+        assertAllReadersAreClosed();
+
         if (indexWriter != null) {
             //TODO Close call can possibly be speeded up by
             //avoiding merge and dropping stuff in memory. To be explored
@@ -178,7 +202,7 @@ public class NRTIndex implements Closeab
 
     @Override
     public String toString() {
-        return definition.getIndexPath();
+        return String.format("%s (%s)", definition.getIndexPath(), getName());
     }
 
     //For test
@@ -186,6 +210,30 @@ public class NRTIndex implements Closeab
         return indexDir;
     }
 
+    private String getName(){
+        return indexDir != null ? indexDir.getName() : "UNKNOWN";
+    }
+
+    private void assertAllReadersAreClosed() {
+        for (IndexReader r : openedReaders){
+            if (r.getRefCount() != 0){
+                String msg = String.format("Unclosed reader found with 
refCount %d for index %s", r.getRefCount(), toString());
+                throw new IllegalStateException(msg);
+            }
+        }
+    }
+
+    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
@@ -212,6 +260,11 @@ public class NRTIndex implements Closeab
                 }
             }
             ctx.stop();
+
+            if (assertAllReadersClosed && result != null && result != 
dirReader) {
+                openedReaders.add(result);
+            }
+
             return result;
         } catch (IOException e) {
             log.warn("Error opening index [{}]", e);
@@ -222,9 +275,7 @@ public class NRTIndex implements Closeab
     private synchronized NRTIndexWriter createWriter() throws IOException {
         String dirName = generateDirName();
         indexDir = indexCopier.getIndexDir(definition, 
definition.getIndexPath(), dirName);
-        Directory fsdir = FSDirectory.open(indexDir);
-        //TODO make these configurable
-        directory = new NRTCachingDirectory(fsdir, 1, 1);
+        directory = directoryFactory.createNRTDir(definition, indexDir);
         IndexWriterConfig config = 
IndexWriterUtils.getIndexWriterConfig(definition, false);
 
         //TODO Explore following for optimizing indexing speed
@@ -232,6 +283,7 @@ public class NRTIndex implements Closeab
         //config.setRAMBufferSizeMB(1024*1024*25);
 
         indexWriter = new IndexWriter(directory, config);
+        log.debug("[{}] Created NRTIndex [{}]", definition.getIndexPath(), 
getName());
         return new NRTIndexWriter(indexWriter);
     }
 

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactory.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactory.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactory.java
 Thu Aug 10 11:13:05 2017
@@ -20,6 +20,7 @@
 package org.apache.jackrabbit.oak.plugins.index.lucene.hybrid;
 
 import java.io.Closeable;
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -32,6 +33,9 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.NRTCachingDirectory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,6 +56,8 @@ public class NRTIndexFactory implements
     private final Clock clock;
     private final long refreshDeltaInSecs;
     private final StatisticsProvider statisticsProvider;
+    private NRTDirectoryFactory directoryFactory = 
DefaultNRTDirFactory.INSTANCE;
+    private boolean assertAllResourcesClosed = 
Boolean.getBoolean("oak.lucene.assertAllResourcesClosed");
 
     public NRTIndexFactory(IndexCopier indexCopier, StatisticsProvider 
statisticsProvider) {
         this(indexCopier, Clock.SIMPLE, REFRESH_DELTA_IN_SECS, 
statisticsProvider);
@@ -75,7 +81,7 @@ public class NRTIndexFactory implements
         }
         String indexPath = definition.getIndexPath();
         NRTIndex current = new NRTIndex(definition, indexCopier, 
getRefreshPolicy(definition),
-                getPrevious(indexPath), statisticsProvider);
+                getPrevious(indexPath), statisticsProvider, directoryFactory, 
assertAllResourcesClosed);
         indexes.put(indexPath, current);
         closeLast(indexPath);
         return current;
@@ -93,6 +99,18 @@ public class NRTIndexFactory implements
         return indexes.get(path);
     }
 
+    public void setDirectoryFactory(NRTDirectoryFactory directoryFactory) {
+        this.directoryFactory = directoryFactory;
+    }
+
+    /**
+     * Test mode upon which enables assertions to confirm that all readers are 
closed
+     * by the time NRTIndex is closed
+     */
+    public void setAssertAllResourcesClosed(boolean assertAllResourcesClosed) {
+        this.assertAllResourcesClosed = assertAllResourcesClosed;
+    }
+
     private void closeLast(String indexPath) {
         List<NRTIndex> existing = indexes.get(indexPath);
         if (existing.size() <= MAX_INDEX_COUNT){
@@ -122,4 +140,15 @@ public class NRTIndexFactory implements
         }
         return new TimedRefreshPolicy(clock, TimeUnit.SECONDS, 
refreshDeltaInSecs);
     }
+
+    private enum DefaultNRTDirFactory implements NRTDirectoryFactory {
+        INSTANCE;
+
+        @Override
+        public Directory createNRTDir(IndexDefinition definition, File 
indexDir) throws IOException {
+            Directory fsdir = FSDirectory.open(indexDir);
+            //TODO make these configurable
+            return new NRTCachingDirectory(fsdir, 1, 1);
+        }
+    }
 }

Copied: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java
 (from r1803951, 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java?p2=jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java&p1=jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java&r1=1803951&r2=1804664&rev=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManagerTest.java
 Thu Aug 10 11:13:05 2017
@@ -46,7 +46,7 @@ import org.junit.rules.TemporaryFolder;
 import static 
com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.PATH;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.newDoc;
-import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
+import static 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 Thu Aug 10 11:13:05 2017
@@ -847,11 +847,11 @@ public class IndexPlannerTest {
     //------ END - Suggestion/spellcheck plan tests
 
     private IndexNode createIndexNode(IndexDefinition defn, long numOfDocs) 
throws IOException {
-        return new IndexNode("foo", defn, new 
TestReaderFactory(createSampleDirectory(numOfDocs)).createReaders(defn, 
EMPTY_NODE, "foo"), null);
+        return new IndexNodeManager("foo", defn, new 
TestReaderFactory(createSampleDirectory(numOfDocs)).createReaders(defn, 
EMPTY_NODE, "foo"), null).acquire();
     }
 
     private IndexNode createIndexNode(IndexDefinition defn) throws IOException 
{
-        return new IndexNode("foo", defn, new 
TestReaderFactory(createSampleDirectory()).createReaders(defn, EMPTY_NODE, 
"foo"), null);
+        return new IndexNodeManager("foo", defn, new 
TestReaderFactory(createSampleDirectory()).createReaders(defn, EMPTY_NODE, 
"foo"), null).acquire();
     }
 
     private FilterImpl createFilter(String nodeTypeName) {

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
 Thu Aug 10 11:13:05 2017
@@ -140,12 +140,12 @@ public class MultiplexingLucenePropertyI
         LuceneIndexReaderFactory readerFactory = new 
DefaultIndexReaderFactory(mip, null);
         List<LuceneIndexReader> readers = readerFactory.createReaders(defn, 
builder.getNodeState(),"/foo");
 
-        IndexNode node = new IndexNode("foo", defn, readers, null);
+        IndexNodeManager node = new IndexNodeManager("foo", defn, readers, 
null);
 
         //3 Obtain the plan
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        IndexPlanner planner = new IndexPlanner(node, "/foo", filter, 
Collections.<QueryIndex.OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node.acquire(), "/foo", 
filter, Collections.<QueryIndex.OrderEntry>emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
         //Count should be sum of both readers

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/HybridIndexTest.java
 Thu Aug 10 11:13:05 2017
@@ -19,9 +19,11 @@
 
 package org.apache.jackrabbit.oak.plugins.index.lucene.hybrid;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.management.ManagementFactory;
 import java.util.Collections;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -30,13 +32,20 @@ import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.PumpStreamHandler;
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
@@ -46,6 +55,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import 
org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexCopier;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants;
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.IndexingMode;
@@ -74,6 +84,10 @@ import org.apache.jackrabbit.oak.spi.whi
 import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.NRTCachingDirectory;
+import org.apache.lucene.store.NoLockFactory;
+import org.apache.lucene.store.SimpleFSDirectory;
 import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
@@ -83,9 +97,12 @@ import static com.google.common.collect.
 import static 
com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
 import static 
org.apache.jackrabbit.oak.spi.mount.Mounts.defaultMountInfoProvider;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
 
 public class HybridIndexTest extends AbstractQueryTest {
     private ExecutorService executorService = Executors.newFixedThreadPool(2);
@@ -93,6 +110,8 @@ public class HybridIndexTest extends Abs
     @Rule
     public TemporaryFolder temporaryFolder = new TemporaryFolder(new 
File("target"));
     private OptionalEditorProvider optionalEditorProvider = new 
OptionalEditorProvider();
+    private NRTIndexFactory nrtIndexFactory;
+    private LuceneIndexProvider luceneIndexProvider;
     private NodeStore nodeStore;
     private DocumentQueue queue;
     private Clock clock = new Clock.Virtual();
@@ -101,8 +120,10 @@ public class HybridIndexTest extends Abs
     private long refreshDelta = TimeUnit.SECONDS.toMillis(1);
 
     @After
-    public void tearDown(){
+    public void tearDown() throws IOException {
+        luceneIndexProvider.close();
         new ExecutorCloser(executorService).close();
+        nrtIndexFactory.close();
     }
 
     @Override
@@ -115,10 +136,11 @@ public class HybridIndexTest extends Abs
         }
         MountInfoProvider mip = defaultMountInfoProvider();
 
-        NRTIndexFactory nrtIndexFactory = new NRTIndexFactory(copier, clock, 
TimeUnit.MILLISECONDS.toSeconds(refreshDelta), StatisticsProvider.NOOP);
+        nrtIndexFactory = new NRTIndexFactory(copier, clock, 
TimeUnit.MILLISECONDS.toSeconds(refreshDelta), StatisticsProvider.NOOP);
+        nrtIndexFactory.setAssertAllResourcesClosed(true);
         LuceneIndexReaderFactory indexReaderFactory = new 
DefaultIndexReaderFactory(mip, copier);
         IndexTracker tracker = new 
IndexTracker(indexReaderFactory,nrtIndexFactory);
-        LuceneIndexProvider provider = new LuceneIndexProvider(tracker);
+        luceneIndexProvider = new LuceneIndexProvider(tracker);
         queue = new DocumentQueue(100, tracker, sameThreadExecutor());
         LuceneIndexEditorProvider editorProvider = new 
LuceneIndexEditorProvider(copier,
                 tracker,
@@ -133,8 +155,8 @@ public class HybridIndexTest extends Abs
         Oak oak = new Oak(nodeStore)
                 .with(new InitialContent())
                 .with(new OpenSecurityProvider())
-                .with((QueryIndexProvider) provider)
-                .with((Observer) provider)
+                .with((QueryIndexProvider) luceneIndexProvider)
+                .with((Observer) luceneIndexProvider)
                 .with(localIndexObserver)
                 .with(editorProvider)
                 .with(new PropertyIndexEditorProvider())
@@ -327,13 +349,93 @@ public class HybridIndexTest extends Abs
         assertQuery(query, of("/b", "/c"));
     }
 
+    @Test
+    public void noFileLeaks() throws Exception{
+        nrtIndexFactory.setDirectoryFactory(new NRTDirectoryFactory() {
+            @Override
+            public Directory createNRTDir(IndexDefinition definition, File 
indexDir) throws IOException {
+                Directory fsdir = new SimpleFSDirectory(indexDir, 
NoLockFactory.getNoLockFactory());
+                //TODO make these configurable
+                return new NRTCachingDirectory(fsdir, 0.001, 0.001);
+            }
+        });
+        String idxName = "hybridtest";
+        Tree idx = createIndex(root.getTree("/"), idxName, 
Collections.singleton("foo"));
+        TestUtil.enableIndexingMode(idx, IndexingMode.SYNC);
+        root.commit();
+        runAsyncIndex();
+
+        createPath("/a").setProperty("foo", "bar");
+        root.commit();
+        runAsyncIndex();
+
+        System.out.printf("Open file count - At start %d%n", 
getOpenFileCount());
+        long fileCount1 = createTestDataAndRunAsync("/content/a", 100);
+        long fileCount2 = createTestDataAndRunAsync("/content/b", 100);
+        long fileCount3 = createTestDataAndRunAsync("/content/c", 100);
+        long fileCount4 = createTestDataAndRunAsync("/content/d", 1);
+        long fileCount5 = createTestDataAndRunAsync("/content/e", 1);
+        System.out.printf("Open file count - At end %d", getOpenFileCount());
+
+        assertThat(fileCount4, lessThanOrEqualTo(fileCount3));
+    }
+
+    private long createTestDataAndRunAsync(String parentPath, int count) 
throws Exception {
+        createTestData(parentPath, count);
+        System.out.printf("Open file count - Post creation of %d nodes at %s 
is %d%n",count, parentPath, getOpenFileCount());
+        runAsyncIndex();
+        long openCount = getOpenFileCount();
+        System.out.printf("Open file count - Post async run at %s is 
%d%n",parentPath, openCount);
+        return openCount;
+    }
+
+    private void createTestData(String parentPath, int count) throws 
CommitFailedException {
+        createPath(parentPath);
+        root.commit();
+
+        for (int i = 0; i < count; i++) {
+            Tree parent = root.getTree(parentPath);
+            Tree t = parent.addChild("testNode"+i);
+            t.setProperty("foo", "bar");
+            root.commit();
+        }
+    }
+
+    private static long getOpenFileCount() throws Exception {
+        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+        ObjectName name = new ObjectName("java.lang:type=OperatingSystem");
+        Long val = null;
+        try {
+            val = (Long) server.getAttribute(name, "OpenFileDescriptorCount");
+        } catch (AttributeNotFoundException e) {
+            //This attribute is only present if the os is unix i.e. when 
UnixOperatingSystemMXBean
+            //is the mbean in use. If running on windows the test would be 
assumed to be true
+            assumeNoException(e);
+        }
+        //dumpOpenFilePaths();
+        return val;
+    }
+
+    private static void dumpOpenFilePaths() throws IOException {
+        String jvmName = ManagementFactory.getRuntimeMXBean().getName();
+        String pid = jvmName.split("@")[0];
+
+        CommandLine cl = new CommandLine("/bin/sh");
+        cl.addArguments(new String[]{"-c", "lsof -p "+pid+" | grep '/nrt'"}, 
false);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DefaultExecutor executor = new DefaultExecutor();
+        executor.setStreamHandler(new PumpStreamHandler(baos));
+        executor.execute(cl);
+        System.out.println(new String(baos.toByteArray()));
+    }
+
     private String explain(String query){
         String explain = "explain " + query;
         return executeQuery(explain, "JCR-SQL2").get(0);
     }
 
     private void runAsyncIndex() {
-        Runnable async = WhiteboardUtils.getService(wb, Runnable.class, new 
Predicate<Runnable>() {
+        AsyncIndexUpdate async = (AsyncIndexUpdate) 
WhiteboardUtils.getService(wb, Runnable.class, new Predicate<Runnable>() {
             @Override
             public boolean apply(@Nullable Runnable input) {
                 return input instanceof AsyncIndexUpdate;
@@ -341,6 +443,9 @@ public class HybridIndexTest extends Abs
         });
         assertNotNull(async);
         async.run();
+        if (async.isFailing()) {
+            fail("AsyncIndexUpdate failed");
+        }
         root.refresh();
     }
 

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactoryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactoryTest.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactoryTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexFactoryTest.java
 Thu Aug 10 11:13:05 2017
@@ -56,6 +56,7 @@ public class NRTIndexFactoryTest {
     public void setUp() throws IOException {
         indexCopier = new IndexCopier(sameThreadExecutor(), 
temporaryFolder.getRoot());
         indexFactory = new NRTIndexFactory(indexCopier, 
StatisticsProvider.NOOP);
+        indexFactory.setAssertAllResourcesClosed(true);
     }
 
     @Test

Modified: 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java?rev=1804664&r1=1804663&r2=1804664&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/hybrid/NRTIndexTest.java
 Thu Aug 10 11:13:05 2017
@@ -66,6 +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);
         LuceneIndexEditorContext.configureUniqueId(builder);
     }
 


Reply via email to