Index: pom.xml =================================================================== --- pom.xml (revision 1875799) +++ pom.xml (working copy) @@ -23,7 +23,7 @@ org.apache.jackrabbit oak-parent - 1.10.8 + 1.10.8-OAK-8978 ../oak-parent/pom.xml Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FacetTestHelper.java =================================================================== Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexNodeManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexNodeManager.java (revision 1875799) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexNodeManager.java (working copy) @@ -64,6 +64,8 @@ private static final PerfLogger PERF_LOGGER = new PerfLogger(LoggerFactory.getLogger(LuceneIndexNodeManager.class.getName() + ".perf")); + public final static String OLD_FACET_PROVIDER_TEST_FAILURE_SLEEP_INSTRUMENT_NAME = "oak.lucene.oldFacetProviderTestFailSleepInstrument"; + private final static int OLD_FACET_PROVIDER_TEST_FAILURE_SLEEP_INSTRUMENT_VALUE = Integer.getInteger(OLD_FACET_PROVIDER_TEST_FAILURE_SLEEP_INSTRUMENT_NAME, 0); static LuceneIndexNodeManager open(String indexPath, NodeState root, NodeState defnNodeState, LuceneIndexReaderFactory readerFactory, @Nullable NRTIndexFactory nrtFactory) @@ -113,10 +115,12 @@ private final Runnable refreshCallback = new Runnable() { @Override public void run() { + //Index reader gets closed when searching facets. This gets triggered in a multi threaded environment. Refer OAK-8898 + FacetTestHelper.sleep(OLD_FACET_PROVIDER_TEST_FAILURE_SLEEP_INSTRUMENT_VALUE); if (refreshLock.tryAcquire()) { try { refreshReaders(); - }finally { + } finally { refreshLock.release(); } } Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (revision 1875799) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (working copy) @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -50,6 +51,7 @@ import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants; import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition; import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.IndexingRule; +import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.SecureFacetConfiguration; import org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexLookup; import org.apache.jackrabbit.oak.plugins.index.lucene.score.ScorerProviderFactory; import org.apache.jackrabbit.oak.plugins.index.lucene.spi.FulltextQueryTermsProvider; @@ -186,6 +188,13 @@ */ public class LucenePropertyIndex extends FulltextIndex { + public final static String OLD_FACET_PROVIDER_CONFIG_NAME = "oak.lucene.oldFacetProvider"; + private final static boolean OLD_FACET_PROVIDER = + Boolean.getBoolean(OLD_FACET_PROVIDER_CONFIG_NAME); + public final static String CACHE_FACET_RESULTS_NAME = "oak.lucene.cacheFacetResults"; + private final static boolean CACHE_FACET_RESULTS = + Boolean.parseBoolean(System.getProperty(CACHE_FACET_RESULTS_NAME, "true")); + private static double MIN_COST = 2.1; private static final Logger LOG = LoggerFactory @@ -209,6 +218,11 @@ private final IndexAugmentorFactory augmentorFactory; + static { + LOG.info(OLD_FACET_PROVIDER_CONFIG_NAME + " = " + OLD_FACET_PROVIDER); + LOG.info(CACHE_FACET_RESULTS_NAME + " = " + CACHE_FACET_RESULTS); + } + public LucenePropertyIndex(IndexTracker tracker) { this(tracker, ScorerProviderFactory.DEFAULT); } @@ -251,7 +265,7 @@ private boolean noDocs = false; private IndexSearcher indexSearcher; private int indexNodeId = -1; - private LuceneFacetProvider facetProvider = null; + private FacetProvider facetProvider; private int rewoundCount = 0; @Override @@ -269,7 +283,7 @@ } private FulltextResultRow convertToRow(ScoreDoc doc, IndexSearcher searcher, Map excerpts, - LuceneFacetProvider facetProvider, + FacetProvider facetProvider, String explanation) throws IOException { IndexReader reader = searcher.getIndexReader(); //TODO Look into usage of field cache for retrieving the path @@ -354,11 +368,18 @@ PERF_LOGGER.end(start, -1, "{} ...", docs.scoreDocs.length); nextBatchSize = (int) Math.min(nextBatchSize * 2L, 100000); - long f = PERF_LOGGER.start(); if (facetProvider == null) { - facetProvider = new LuceneFacetProvider( - FacetHelper.getFacets(searcher, query, plan, indexNode.getDefinition().getSecureFacetConfiguration()) - ); + long f = PERF_LOGGER.start(); + if (OLD_FACET_PROVIDER) { + // here the current searcher gets referenced for later + // but the searcher might get closed in the meantime + facetProvider = new LuceneFacetProvider( + FacetHelper.getFacets(searcher, query, plan, indexNode.getDefinition().getSecureFacetConfiguration()) + ); + } else { + // a new searcher is opened and closed when needed + facetProvider = new DelayedLuceneFacetProvider(LucenePropertyIndex.this, query, plan, indexNode.getDefinition().getSecureFacetConfiguration()); + } PERF_LOGGER.end(f, -1, "facets retrieved"); } @@ -1548,6 +1569,59 @@ return Iterators.concat(propIndex.iterator(), itr); } + static class DelayedLuceneFacetProvider implements FacetProvider { + private final LucenePropertyIndex index; + private final Query query; + private final IndexPlan plan; + private final SecureFacetConfiguration config; + private final HashMap> cachedResults = new HashMap<>(); + + DelayedLuceneFacetProvider(LucenePropertyIndex index, Query query, IndexPlan plan, SecureFacetConfiguration config) { + this.index = index; + this.query = query; + this.plan = plan; + this.config = config; + } + + @Override + public List getFacets(int numberOfFacets, String columnName) throws IOException { + if (!CACHE_FACET_RESULTS) { + return getFacetsUncached(numberOfFacets, columnName); + } + String cacheKey = columnName + "/" + numberOfFacets; + if (cachedResults.containsKey(cacheKey)) { + return cachedResults.get(cacheKey); + } + List result = getFacetsUncached(numberOfFacets, columnName); + cachedResults.put(cacheKey, result); + return result; + } + + private List getFacetsUncached(int numberOfFacets, String columnName) throws IOException { + LuceneIndexNode indexNode = index.acquireIndexNode(plan); + try { + IndexSearcher searcher = indexNode.getSearcher(); + String facetFieldName = FulltextIndex.parseFacetField(columnName); + Facets facets = FacetHelper.getFacets(searcher, query, plan, config); + if (facets != null) { + ImmutableList.Builder res = new ImmutableList.Builder<>(); + FacetResult topChildren = facets.getTopChildren(numberOfFacets, facetFieldName); + if (topChildren != null) { + for (LabelAndValue lav : topChildren.labelValues) { + res.add(new Facet( + lav.label, lav.value.intValue() + )); + } + return res.build(); + } + } + return null; + } finally { + indexNode.release(); + } + } + } + static class LuceneFacetProvider implements FacetProvider { private final Facets facets;