You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by br...@apache.org on 2020/10/25 09:59:24 UTC
[lucene-solr] branch master updated: LUCENE-9455: ExitableTermsEnum
should sample timeout and interruption check before calling next()
This is an automated email from the ASF dual-hosted git repository.
broustant pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/master by this push:
new 98594b5 LUCENE-9455: ExitableTermsEnum should sample timeout and interruption check before calling next()
98594b5 is described below
commit 98594b54fdaca500b7b9ead82082e3b3da152f4c
Author: Zach Chen <za...@yahoo.com>
AuthorDate: Sat Oct 24 21:46:24 2020 +0200
LUCENE-9455: ExitableTermsEnum should sample timeout and interruption check before calling next()
Closes #1998
---
lucene/CHANGES.txt | 3 +
.../lucene/index/ExitableDirectoryReader.java | 26 ++++----
.../lucene/index/TestExitableDirectoryReader.java | 75 ++++++++++++++++++----
3 files changed, 82 insertions(+), 22 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 1927b58..41e96aa 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -156,6 +156,9 @@ Improvements
* LUCENE-9531: Consolidated CharStream and FastCharStream classes: these have been moved
from each query parser package to org.apache.lucene.queryparser.charstream (Dawid Weiss).
+* LUCENE-9455: ExitableTermsEnum should sample timeout and interruption
+ check before calling next(). (Zach Chen via Bruno Roustant)
+
Bug fixes
* LUCENE-8663: NRTCachingDirectory.slowFileExists may open a file while
diff --git a/lucene/core/src/java/org/apache/lucene/index/ExitableDirectoryReader.java b/lucene/core/src/java/org/apache/lucene/index/ExitableDirectoryReader.java
index b52189b..ecedb8c 100644
--- a/lucene/core/src/java/org/apache/lucene/index/ExitableDirectoryReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/ExitableDirectoryReader.java
@@ -496,35 +496,39 @@ public class ExitableDirectoryReader extends FilterDirectoryReader {
* exitable enumeration of terms.
*/
public static class ExitableTermsEnum extends FilterTermsEnum {
-
+ // Create bit mask in the form of 0000 1111 for efficient checking
+ private static final int NUM_CALLS_PER_TIMEOUT_CHECK = (1 << 4) - 1; // 15
+ private int calls;
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableTermsEnum(TermsEnum termsEnum, QueryTimeout queryTimeout) {
super(termsEnum);
this.queryTimeout = queryTimeout;
- checkAndThrow();
+ checkTimeoutWithSampling();
}
/**
* Throws {@link ExitingReaderException} if {@link QueryTimeout#shouldExit()} returns true,
* or if {@link Thread#interrupted()} returns true.
*/
- private void checkAndThrow() {
- if (queryTimeout.shouldExit()) {
- throw new ExitingReaderException("The request took too long to iterate over terms. Timeout: "
- + queryTimeout.toString()
- + ", TermsEnum=" + in
- );
- } else if (Thread.interrupted()) {
- throw new ExitingReaderException("Interrupted while iterating over terms. TermsEnum=" + in);
+ private void checkTimeoutWithSampling() {
+ if ((calls++ & NUM_CALLS_PER_TIMEOUT_CHECK) == 0) {
+ if (queryTimeout.shouldExit()) {
+ throw new ExitingReaderException("The request took too long to iterate over terms. Timeout: "
+ + queryTimeout.toString()
+ + ", TermsEnum=" + in
+ );
+ } else if (Thread.interrupted()) {
+ throw new ExitingReaderException("Interrupted while iterating over terms. TermsEnum=" + in);
+ }
}
}
@Override
public BytesRef next() throws IOException {
// Before every iteration, check if the iteration should exit
- checkAndThrow();
+ checkTimeoutWithSampling();
return in.next();
}
}
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestExitableDirectoryReader.java b/lucene/core/src/test/org/apache/lucene/index/TestExitableDirectoryReader.java
index e1e45f0..f43ebfe 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestExitableDirectoryReader.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestExitableDirectoryReader.java
@@ -16,18 +16,8 @@
*/
package org.apache.lucene.index;
-import java.io.IOException;
-import java.util.Arrays;
-
import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.BinaryDocValuesField;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.IntPoint;
-import org.apache.lucene.document.NumericDocValuesField;
-import org.apache.lucene.document.SortedDocValuesField;
-import org.apache.lucene.document.SortedNumericDocValuesField;
-import org.apache.lucene.document.SortedSetDocValuesField;
+import org.apache.lucene.document.*;
import org.apache.lucene.index.ExitableDirectoryReader.ExitingReaderException;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
@@ -37,6 +27,9 @@ import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.LuceneTestCase;
+import java.io.IOException;
+import java.util.Arrays;
+
/**
* Test that uses a default/lucene Implementation of {@link QueryTimeout}
* to exit out long running queries that take too long to iterate over Terms.
@@ -167,6 +160,47 @@ public class TestExitableDirectoryReader extends LuceneTestCase {
}
/**
+ * Tests time out check sampling of TermsEnum iterations
+ *
+ * @throws Exception on error
+ */
+ public void testExitableTermsEnumSampleTimeoutCheck() throws Exception {
+ try (Directory directory = newDirectory()) {
+ try (IndexWriter writer = new IndexWriter(directory, newIndexWriterConfig(new MockAnalyzer(random())))) {
+ for (int i = 0; i < 50; i++) {
+ Document d1 = new Document();
+ d1.add(newTextField("default", "term" + i, Field.Store.YES));
+ writer.addDocument(d1);
+ }
+
+ writer.forceMerge(1);
+ writer.commit();
+
+ DirectoryReader directoryReader;
+ DirectoryReader exitableDirectoryReader;
+ IndexReader reader;
+ IndexSearcher searcher;
+
+ Query query = new PrefixQuery(new Term("default", "term"));
+
+ // Set a fairly high timeout value (infinite) and expect the query to complete in that time frame.
+ // Not checking the validity of the result, but checking the sampling kicks in to reduce the number of timeout check
+ CountingQueryTimeout queryTimeout = new CountingQueryTimeout();
+ directoryReader = DirectoryReader.open(directory);
+ exitableDirectoryReader = new ExitableDirectoryReader(directoryReader, queryTimeout);
+ reader = new TestReader(getOnlyLeafReader(exitableDirectoryReader));
+ searcher = new IndexSearcher(reader);
+ searcher.search(query, 300);
+ reader.close();
+ // The number of sampled query time out check here depends on two factors:
+ // 1. ExitableDirectoryReader.ExitableTermsEnum.NUM_CALLS_PER_TIMEOUT_CHECK
+ // 2. MultiTermQueryConstantScoreWrapper.BOOLEAN_REWRITE_TERM_COUNT_THRESHOLD
+ assertEquals(5, queryTimeout.getShouldExitCallCount());
+ }
+ }
+ }
+
+ /**
* Tests timing out of PointValues queries
*
* @throws Exception on error
@@ -268,6 +302,25 @@ public class TestExitableDirectoryReader extends LuceneTestCase {
};
}
+ private static class CountingQueryTimeout implements QueryTimeout {
+ private int counter = 0;
+
+ @Override
+ public boolean shouldExit() {
+ counter++;
+ return false;
+ }
+
+ @Override
+ public boolean isTimeoutEnabled() {
+ return true;
+ }
+
+ public int getShouldExitCallCount() {
+ return counter;
+ }
+ }
+
private static QueryTimeout immediateQueryTimeout() {
return new QueryTimeout() {