diff --git a/lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java b/lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java index 03d4a29ae73..3479a55e6a9 100644 --- a/lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java +++ b/lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java @@ -139,25 +139,21 @@ public class LRUQueryCache implements QueryCache, Accountable { /** * Create a new instance that will cache at most maxSize queries with at most * maxRamBytesUsed bytes of memory. Queries will only be cached on leaves that have more - * than 10k documents and have more than 3% of the total number of documents in the index. This - * should guarantee that all leaves from the upper {@link TieredMergePolicy tier} will be cached - * while ensuring that at most 33 leaves can make it to the cache (very likely less - * than 10 in practice), which is useful for this implementation since some operations perform in - * linear time with the number of cached leaves. Only clauses whose cost is at most 100x the cost - * of the top-level query will be cached in order to not hurt latency too much because of caching. + * than 10k documents and have more than half of the average documents per leave of the index. + * This should guarantee that all leaves from the upper {@link TieredMergePolicy tier} will be + * cached. Only clauses whose cost is at most 100x the cost of the top-level query will be cached + * in order to not hurt latency too much because of caching. */ public LRUQueryCache(int maxSize, long maxRamBytesUsed) { - this(maxSize, maxRamBytesUsed, new MinSegmentSizePredicate(10000, .03f), 10); + this(maxSize, maxRamBytesUsed, new MinSegmentSizePredicate(10000), 10); } // pkg-private for testing static class MinSegmentSizePredicate implements Predicate { private final int minSize; - private final float minSizeRatio; - MinSegmentSizePredicate(int minSize, float minSizeRatio) { + MinSegmentSizePredicate(int minSize) { this.minSize = minSize; - this.minSizeRatio = minSizeRatio; } @Override @@ -167,8 +163,9 @@ public class LRUQueryCache implements QueryCache, Accountable { return false; } final IndexReaderContext topLevelContext = ReaderUtil.getTopLevelContext(context); - final float sizeRatio = (float) context.reader().maxDoc() / topLevelContext.reader().maxDoc(); - return sizeRatio >= minSizeRatio; + final int averageTotalDocs = + topLevelContext.reader().maxDoc() / topLevelContext.leaves().size(); + return maxDoc * 2 > averageTotalDocs; } } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java index e90be74e234..da68d13c669 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java @@ -20,8 +20,10 @@ import static org.apache.lucene.util.RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_E import static org.apache.lucene.util.RamUsageEstimator.LINKED_HASHTABLE_RAM_BYTES_PER_ENTRY; import static org.apache.lucene.util.RamUsageEstimator.QUERY_DEFAULT_RAM_BYTES_USED; +import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -39,6 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.IntConsumer; import java.util.stream.Collectors; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field.Store; @@ -1387,46 +1390,45 @@ public class TestLRUQueryCache extends LuceneTestCase { public void testMinSegmentSizePredicate() throws IOException { Directory dir = newDirectory(); - IndexWriterConfig iwc = newIndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE); - RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); - w.addDocument(new Document()); - DirectoryReader reader = w.getReader(); - IndexSearcher searcher = newSearcher(reader); - searcher.setQueryCachingPolicy(ALWAYS_CACHE); - - LRUQueryCache cache = - new LRUQueryCache( - 2, 10000, new LRUQueryCache.MinSegmentSizePredicate(2, 0f), Float.POSITIVE_INFINITY); - searcher.setQueryCache(cache); - searcher.count(new DummyQuery()); - assertEquals(0, cache.getCacheCount()); - - cache = - new LRUQueryCache( - 2, 10000, new LRUQueryCache.MinSegmentSizePredicate(1, 0f), Float.POSITIVE_INFINITY); - searcher.setQueryCache(cache); - searcher.count(new DummyQuery()); - assertEquals(1, cache.getCacheCount()); - - cache = - new LRUQueryCache( - 2, 10000, new LRUQueryCache.MinSegmentSizePredicate(0, .6f), Float.POSITIVE_INFINITY); - searcher.setQueryCache(cache); - searcher.count(new DummyQuery()); - assertEquals(1, cache.getCacheCount()); - - w.addDocument(new Document()); - reader.close(); - reader = w.getReader(); - searcher = newSearcher(reader); - searcher.setQueryCachingPolicy(ALWAYS_CACHE); - cache = - new LRUQueryCache( - 2, 10000, new LRUQueryCache.MinSegmentSizePredicate(0, .6f), Float.POSITIVE_INFINITY); - searcher.setQueryCache(cache); - searcher.count(new DummyQuery()); - assertEquals(0, cache.getCacheCount()); - + IndexWriterConfig iwc = new IndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE); + IndexWriter w = new IndexWriter(dir, iwc); + IntConsumer newSegment = + numDocs -> { + try { + for (int i = 0; i < numDocs; i++) { + w.addDocument(new Document()); + } + w.flush(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + newSegment.accept(1); + newSegment.accept(4); + newSegment.accept(10); + newSegment.accept(35); + int numLargeSegments = RandomNumbers.randomIntBetween(random(), 2, 40); + for (int i = 0; i < numLargeSegments; i++) { + newSegment.accept(RandomNumbers.randomIntBetween(random(), 50, 55)); + } + DirectoryReader reader = DirectoryReader.open(w); + for (int i = 0; i < 3; i++) { + var predicate = + new LRUQueryCache.MinSegmentSizePredicate( + RandomNumbers.randomIntBetween(random(), 1, Integer.MAX_VALUE)); + assertFalse(predicate.test(reader.leaves().get(i))); + } + for (int i = 3; i < reader.leaves().size(); i++) { + var leaf = reader.leaves().get(i); + var small = + new LRUQueryCache.MinSegmentSizePredicate( + RandomNumbers.randomIntBetween(random(), 60, Integer.MAX_VALUE)); + assertFalse(small.test(leaf)); + var big = + new LRUQueryCache.MinSegmentSizePredicate( + RandomNumbers.randomIntBetween(random(), 10, 30)); + assertTrue(big.test(leaf)); + } reader.close(); w.close(); dir.close(); diff --git a/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java b/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java index 059d3e7264a..a764daf9d5b 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java @@ -75,11 +75,7 @@ public class TestUsageTrackingFilterCachingPolicy extends LuceneTestCase { IndexSearcher searcher = new IndexSearcher(reader); UsageTrackingQueryCachingPolicy policy = new UsageTrackingQueryCachingPolicy(); LRUQueryCache cache = - new LRUQueryCache( - 10, - Long.MAX_VALUE, - new LRUQueryCache.MinSegmentSizePredicate(1, 0f), - Float.POSITIVE_INFINITY); + new LRUQueryCache(10, Long.MAX_VALUE, ctx -> true, Float.POSITIVE_INFINITY); searcher.setQueryCache(cache); searcher.setQueryCachingPolicy(policy);