Skip to content

Commit

Permalink
Add a cluster setting to disallow slow queries
Browse files Browse the repository at this point in the history
Add a new cluster setting `search.disallow_slow_queries` which by
default is `false`. If set to `true` then certain queries (prefix,
fuzzy, regexp and wildcard) that have usually slow performance cannot
be executed and an exception is thrown.

Closes: elastic#29050
  • Loading branch information
matriv committed Jan 24, 2020
1 parent 70729f3 commit d9a14a6
Show file tree
Hide file tree
Showing 31 changed files with 496 additions and 56 deletions.
10 changes: 9 additions & 1 deletion docs/reference/query-dsl.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ or to alter their behaviour (such as the

Query clauses behave differently depending on whether they are used in
<<query-filter-context,query context or filter context>>.

[[query-dsl-disallow-slow]]
== Disallow slow queries
Certain queries like <<query-dsl-fuzzy-query,`fuzzy`>>, <<query-dsl-prefix-query,`prefix`>>,
<<query-dsl-regexp-query,`regexp`>> and <<query-dsl-bool-query,`wildcard`>> ,
that are usually slow performance can affect the cluster performance.
The execution of such queries can be prevented by setting the value of the `search.disallow_slow_queries`
setting to `true` (defaults tp `false`).
--

include::query-dsl/query_filter_context.asciidoc[]
Expand All @@ -51,4 +59,4 @@ include::query-dsl/minimum-should-match.asciidoc[]

include::query-dsl/multi-term-rewrite.asciidoc[]

include::query-dsl/regexp-syntax.asciidoc[]
include::query-dsl/regexp-syntax.asciidoc[]
6 changes: 5 additions & 1 deletion docs/reference/query-dsl/fuzzy-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,8 @@ adjacent characters (ab → ba). Defaults to `true`.

`rewrite`::
(Optional, string) Method used to rewrite the query. For valid values and more
information, see the <<query-dsl-multi-term-rewrite, `rewrite` parameter>>.
information, see the <<query-dsl-multi-term-rewrite, `rewrite` parameter>>.

==== Notes
Fuzzy queries will not be executed if <<query-dsl-disallow-slow, `search.disallow_slow_queries`>>
is set to true.
10 changes: 9 additions & 1 deletion docs/reference/query-dsl/prefix-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ GET /_search
You can speed up prefix queries using the <<index-prefixes,`index_prefixes`>>
mapping parameter. If enabled, {es} indexes prefixes between 2 and 5
characters in a separate field. This lets {es} run prefix queries more
efficiently at the cost of a larger index.
efficiently at the cost of a larger index.

[[prefix-query-disallow-slow]]
===== Disallow slow queries
Prefix queries will not be executed if <<query-dsl-disallow-slow, `search.disallow_slow_queries`>>
is set to true. If <<index-prefixes, `index prefixes`>> are enabled though, an optimised query is
built, which is not considered slow and is executed despite of the fact that the
<<query-dsl-disallow-slow, `search.disallow_slow_queries`>> set to true.

6 changes: 6 additions & 0 deletions docs/reference/query-dsl/query-string-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,9 @@ The example above creates a boolean query:
`(blended(terms:[field2:this, field1:this]) blended(terms:[field2:that, field1:that]) blended(terms:[field2:thus, field1:thus]))~2`

that matches documents with at least two of the three per-term blended queries.

==== Notes
===== Disallow slow queries
Query string query can be internally be transformed to a <<query-dsl-prefix-query, `prefix query`>> which means
that if the prefix queries are disabled as explained <<prefix-query-disallow-slow, here>> the query will not be
executed and an exception will be thrown.
5 changes: 5 additions & 0 deletions docs/reference/query-dsl/regexp-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ regular expressions.
`rewrite`::
(Optional, string) Method used to rewrite the query. For valid values and more
information, see the <<query-dsl-multi-term-rewrite, `rewrite` parameter>>.

==== Notes
===== Disallow slow queries
Regexp queries will not be executed if <<query-dsl-disallow-slow, `search.disallow_slow_queries`>>
is set to true.
7 changes: 6 additions & 1 deletion docs/reference/query-dsl/wildcard-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ increases the relevance score.

`rewrite`::
(Optional, string) Method used to rewrite the query. For valid values and more information, see the
<<query-dsl-multi-term-rewrite, `rewrite` parameter>>.
<<query-dsl-multi-term-rewrite, `rewrite` parameter>>.

==== Notes
===== Disallow slow queries
Wildcard queries will not be executed if <<query-dsl-disallow-slow, `search.disallow_slow_queries`>>
is set to true.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.Defaults;
import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.PrefixFieldType;
import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.SearchAsYouTypeFieldType;
Expand Down Expand Up @@ -100,14 +101,19 @@ public void testPrefixQuery() {

// this term should be a length that can be rewriteable to a term query on the prefix field
final String withinBoundsTerm = "foo";
assertThat(fieldType.prefixQuery(withinBoundsTerm, CONSTANT_SCORE_REWRITE, null),
assertThat(fieldType.prefixQuery(withinBoundsTerm, CONSTANT_SCORE_REWRITE, randomMockShardContext()),
equalTo(new ConstantScoreQuery(new TermQuery(new Term(PREFIX_NAME, withinBoundsTerm)))));

// our defaults don't allow a situation where a term can be too small

// this term should be too long to be rewriteable to a term query on the prefix field
final String longTerm = "toolongforourprefixfieldthistermis";
assertThat(fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, null),
assertThat(fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_QSC),
equalTo(new PrefixQuery(new Term(NAME, longTerm))));

ElasticsearchException ee = expectThrows(ElasticsearchException.class,
() -> fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_QSC_DISALLOW_SLOW));
assertEquals("prefix queries cannot be executed when 'search.disallow_slow_queries' is set to true",
ee.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected BytesRef indexedValueForSearch(Object value) {

@Override
public Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLength, int maxExpansions,
boolean transpositions) {
boolean transpositions, QueryShardContext context) {
throw new UnsupportedOperationException("[fuzzy] queries are not supported on [" + CONTENT_TYPE + "] fields.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,31 +102,31 @@ public void testRegexpQuery() {
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
expectThrows(UnsupportedOperationException.class,
() -> ft.regexpQuery("foo.*", 0, 10, null, null));
() -> ft.regexpQuery("foo.*", 0, 10, null, randomMockShardContext()));
}

public void testFuzzyQuery() {
MappedFieldType ft = createDefaultFieldType();
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
expectThrows(UnsupportedOperationException.class,
() -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true));
() -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true, randomMockShardContext()));
}

public void testPrefixQuery() {
MappedFieldType ft = createDefaultFieldType();
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
expectThrows(UnsupportedOperationException.class,
() -> ft.prefixQuery("prefix", null, null));
() -> ft.prefixQuery("prefix", null, randomMockShardContext()));
}

public void testWildcardQuery() {
MappedFieldType ft = createDefaultFieldType();
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
expectThrows(UnsupportedOperationException.class,
() -> ft.wildcardQuery("foo*", null, null));
() -> ft.wildcardQuery("foo*", null, randomMockShardContext()));
}

public void testRangeQuery() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
setup:
- skip:
version: " - 7.99.99"
reason: "implemented in 8.0.0"

- do:
indices.create:
index: test
body:
mappings:
properties:
text:
type: text
analyzer: standard
- do:
bulk:
refresh: true
body:
- '{"index": {"_index": "test", "_id": "1"}}'
- '{"text" : "Some like it hot, some like it cold"}'
- '{"index": {"_index": "test", "_id": "2"}}'
- '{"text" : "Its cold outside, theres no kind of atmosphere"}'
- '{"index": {"_index": "test", "_id": "3"}}'
- '{"text" : "Baby its cold there outside"}'
- '{"index": {"_index": "test", "_id": "4"}}'
- '{"text" : "Outside it is cold and wet"}'

---
teardown:
- skip:
version: " - 7.99.99"
reason: "implemented in 8.0.0"

- do:
cluster.put_settings:
body:
transient:
search.disallow_slow_queries: null

---
"Test disallow slow queries":
- skip:
version: " - 7.99.99"
reason: "implemented in 8.0.0"

### Check for initial setting = null -> false
- do:
cluster.get_settings:
flat_settings: true

- match: {search.disallow_slow_queries: null}

### Prefix
- do:
search:
index: test
body:
query:
prefix:
text:
value: out

- match: { hits.total.value: 3 }

### Fuzzy
- do:
search:
index: test
body:
query:
fuzzy:
text:
value: outwide

- match: { hits.total.value: 3 }


### Regexp
- do:
search:
index: test
body:
query:
regexp:
text:
value: .*ou.*id.*

- match: { hits.total.value: 3 }

### Wildcard
- do:
search:
index: test
body:
query:
wildcard:
text:
value: out?ide

- match: { hits.total.value: 3 }

### Update setting to true
- do:
cluster.put_settings:
body:
transient:
search.disallow_slow_queries: "true"
flat_settings: true

- match: {transient: {search.disallow_slow_queries: "true"}}

### Prefix
- do:
catch: /prefix queries cannot be executed when \'search.disallow_slow_queries\' is set to true/
search:
index: test
body:
query:
prefix:
text:
value: out

### Fuzzy
- do:
catch: /fuzzy queries cannot be executed when \'search.disallow_slow_queries\' is set to true/
search:
index: test
body:
query:
fuzzy:
text:
value: outwide

### Regexp
- do:
catch: /regexp queries cannot be executed when \'search.disallow_slow_queries\' is set to true/
search:
index: test
body:
query:
regexp:
text:
value: .*ou.*id.*

### Wildcard
- do:
catch: /wildcard queries cannot be executed when \'search.disallow_slow_queries\' is set to true/
search:
index: test
body:
query:
wildcard:
text:
value: out?ide

### Revert setting to false
- do:
cluster.put_settings:
body:
transient:
search.disallow_slow_queries: "false"
flat_settings: true

- match: {transient: {search.disallow_slow_queries: "false"}}

### Prefix
- do:
search:
index: test
body:
query:
prefix:
text:
value: out

- match: { hits.total.value: 3 }

### Fuzzy
- do:
search:
index: test
body:
query:
fuzzy:
text:
value: outwide

- match: { hits.total.value: 3 }

### Regexp
- do:
search:
index: test
body:
query:
regexp:
text:
value: .*ou.*id.*

- match: { hits.total.value: 3 }

### Wildcard
- do:
search:
index: test
body:
query:
wildcard:
text:
value: out?ide

- match: { hits.total.value: 3 }
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ public void apply(Settings value, Settings current, Settings previous) {
SearchService.DEFAULT_KEEPALIVE_SETTING,
SearchService.KEEPALIVE_INTERVAL_SETTING,
SearchService.MAX_KEEPALIVE_SETTING,
SearchService.DISALLOW_SLOW_QUERIES,
MultiBucketConsumerService.MAX_BUCKET_SETTING,
SearchService.LOW_LEVEL_CANCELLATION_SETTING,
SearchService.MAX_OPEN_SCROLL_CONTEXT,
Expand Down
Loading

0 comments on commit d9a14a6

Please sign in to comment.