Skip to content

Commit e379d20

Browse files
committed
Merge pull request #12211 from jasontedor/features/12149
Add global search timeout setting
2 parents ba16dec + d1c8703 commit e379d20

File tree

9 files changed

+64
-10
lines changed

9 files changed

+64
-10
lines changed

core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/TransportValidateQueryAction.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.elasticsearch.index.shard.IndexShard;
4747
import org.elasticsearch.indices.IndicesService;
4848
import org.elasticsearch.script.ScriptService;
49+
import org.elasticsearch.search.SearchService;
4950
import org.elasticsearch.search.internal.DefaultSearchContext;
5051
import org.elasticsearch.search.internal.SearchContext;
5152
import org.elasticsearch.search.internal.ShardSearchLocalRequest;
@@ -172,7 +173,8 @@ protected ShardValidateQueryResponse shardOperation(ShardValidateQueryRequest re
172173
DefaultSearchContext searchContext = new DefaultSearchContext(0,
173174
new ShardSearchLocalRequest(request.types(), request.nowInMillis(), request.filteringAliases()),
174175
null, searcher, indexService, indexShard,
175-
scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher
176+
scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher,
177+
SearchService.NO_TIMEOUT
176178
);
177179
SearchContext.setCurrent(searchContext);
178180
try {

core/src/main/java/org/elasticsearch/action/exists/TransportExistsAction.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.elasticsearch.index.shard.IndexShard;
4646
import org.elasticsearch.indices.IndicesService;
4747
import org.elasticsearch.script.ScriptService;
48+
import org.elasticsearch.search.SearchService;
4849
import org.elasticsearch.search.SearchShardTarget;
4950
import org.elasticsearch.search.internal.DefaultSearchContext;
5051
import org.elasticsearch.search.internal.SearchContext;
@@ -153,7 +154,9 @@ protected ShardExistsResponse shardOperation(ShardExistsRequest request) {
153154
SearchContext context = new DefaultSearchContext(0,
154155
new ShardSearchLocalRequest(request.types(), request.nowInMillis(), request.filteringAliases()),
155156
shardTarget, indexShard.acquireSearcher("exists"), indexService, indexShard,
156-
scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher);
157+
scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher,
158+
SearchService.NO_TIMEOUT
159+
);
157160
SearchContext.setCurrent(context);
158161

159162
try {

core/src/main/java/org/elasticsearch/action/explain/TransportExplainAction.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.elasticsearch.index.shard.ShardId;
4444
import org.elasticsearch.indices.IndicesService;
4545
import org.elasticsearch.script.ScriptService;
46+
import org.elasticsearch.search.SearchService;
4647
import org.elasticsearch.search.internal.DefaultSearchContext;
4748
import org.elasticsearch.search.internal.SearchContext;
4849
import org.elasticsearch.search.internal.ShardSearchLocalRequest;
@@ -114,7 +115,8 @@ protected ExplainResponse shardOperation(ExplainRequest request, ShardId shardId
114115
0, new ShardSearchLocalRequest(new String[]{request.type()}, request.nowInMillis, request.filteringAlias()),
115116
null, result.searcher(), indexService, indexShard,
116117
scriptService, pageCacheRecycler,
117-
bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher
118+
bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher,
119+
SearchService.NO_TIMEOUT
118120
);
119121
SearchContext.setCurrent(context);
120122

core/src/main/java/org/elasticsearch/cluster/settings/ClusterDynamicSettingsModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.indices.recovery.RecoverySettings;
3535
import org.elasticsearch.indices.store.IndicesStore;
3636
import org.elasticsearch.indices.ttl.IndicesTTLService;
37+
import org.elasticsearch.search.SearchService;
3738
import org.elasticsearch.threadpool.ThreadPool;
3839

3940
/**
@@ -100,6 +101,7 @@ public ClusterDynamicSettingsModule() {
100101
clusterDynamicSettings.addDynamicSetting(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_LIMIT_SETTING, Validator.MEMORY_SIZE);
101102
clusterDynamicSettings.addDynamicSetting(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_OVERHEAD_SETTING, Validator.NON_NEGATIVE_DOUBLE);
102103
clusterDynamicSettings.addDynamicSetting(InternalClusterService.SETTING_CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD, Validator.TIME_NON_NEGATIVE);
104+
clusterDynamicSettings.addDynamicSetting(SearchService.DEFAULT_SEARCH_TIMEOUT, Validator.TIMEOUT);
103105
}
104106

105107
public void addDynamicSettings(String... settings) {

core/src/main/java/org/elasticsearch/cluster/settings/Validator.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ public String validate(String setting, String value) {
5757
}
5858
};
5959

60+
public static final Validator TIMEOUT = new Validator() {
61+
@Override
62+
public String validate(String setting, String value) {
63+
try {
64+
if (value == null) {
65+
throw new NullPointerException("value must not be null");
66+
}
67+
TimeValue timeValue = TimeValue.parseTimeValue(value, null, setting);
68+
assert timeValue != null;
69+
if (timeValue.millis() < 0 && timeValue.millis() != -1) {
70+
return "cannot parse value [" + value + "] as a timeout";
71+
}
72+
} catch (ElasticsearchParseException ex) {
73+
return ex.getMessage();
74+
}
75+
return null;
76+
}
77+
};
78+
6079
public static final Validator TIME_NON_NEGATIVE = new Validator() {
6180
@Override
6281
public String validate(String setting, String value) {

core/src/main/java/org/elasticsearch/search/SearchService.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.elasticsearch.indices.IndicesWarmer.TerminationHandle;
7676
import org.elasticsearch.indices.IndicesWarmer.WarmerContext;
7777
import org.elasticsearch.indices.cache.request.IndicesRequestCache;
78+
import org.elasticsearch.node.settings.NodeSettingsService;
7879
import org.elasticsearch.script.ExecutableScript;
7980
import org.elasticsearch.script.Script.ScriptParseException;
8081
import org.elasticsearch.script.ScriptContext;
@@ -101,6 +102,7 @@
101102
import java.util.concurrent.atomic.AtomicLong;
102103

103104
import static org.elasticsearch.common.Strings.hasLength;
105+
import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
104106
import static org.elasticsearch.common.unit.TimeValue.timeValueMinutes;
105107

106108
/**
@@ -111,7 +113,9 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
111113
public static final String NORMS_LOADING_KEY = "index.norms.loading";
112114
public static final String DEFAULT_KEEPALIVE_KEY = "search.default_keep_alive";
113115
public static final String KEEPALIVE_INTERVAL_KEY = "search.keep_alive_interval";
116+
public static final String DEFAULT_SEARCH_TIMEOUT = "search.default_search_timeout";
114117

118+
public static final TimeValue NO_TIMEOUT = timeValueMillis(-1);
115119

116120
private final ThreadPool threadPool;
117121

@@ -137,6 +141,8 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
137141

138142
private final long defaultKeepAlive;
139143

144+
private volatile TimeValue defaultSearchTimeout;
145+
140146
private final ScheduledFuture<?> keepAliveReaper;
141147

142148
private final AtomicLong idGenerator = new AtomicLong();
@@ -148,7 +154,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
148154
private final ParseFieldMatcher parseFieldMatcher;
149155

150156
@Inject
151-
public SearchService(Settings settings, ClusterService clusterService, IndicesService indicesService,IndicesWarmer indicesWarmer, ThreadPool threadPool,
157+
public SearchService(Settings settings, NodeSettingsService nodeSettingsService, ClusterService clusterService, IndicesService indicesService,IndicesWarmer indicesWarmer, ThreadPool threadPool,
152158
ScriptService scriptService, PageCacheRecycler pageCacheRecycler, BigArrays bigArrays, DfsPhase dfsPhase, QueryPhase queryPhase, FetchPhase fetchPhase,
153159
IndicesRequestCache indicesQueryCache) {
154160
super(settings);
@@ -202,6 +208,20 @@ public void afterIndexDeleted(Index index, @IndexSettings Settings indexSettings
202208
this.indicesWarmer.addListener(new NormsWarmer());
203209
this.indicesWarmer.addListener(new FieldDataWarmer());
204210
this.indicesWarmer.addListener(new SearchWarmer());
211+
212+
defaultSearchTimeout = settings.getAsTime(DEFAULT_SEARCH_TIMEOUT, NO_TIMEOUT);
213+
nodeSettingsService.addListener(new SearchSettingsListener());
214+
}
215+
216+
class SearchSettingsListener implements NodeSettingsService.Listener {
217+
@Override
218+
public void onRefreshSettings(Settings settings) {
219+
final TimeValue maybeNewDefaultSearchTimeout = settings.getAsTime(SearchService.DEFAULT_SEARCH_TIMEOUT, SearchService.this.defaultSearchTimeout);
220+
if (!maybeNewDefaultSearchTimeout.equals(SearchService.this.defaultSearchTimeout)) {
221+
logger.info("updating [{}] from [{}] to [{}]", SearchService.DEFAULT_SEARCH_TIMEOUT, SearchService.this.defaultSearchTimeout, maybeNewDefaultSearchTimeout);
222+
SearchService.this.defaultSearchTimeout = maybeNewDefaultSearchTimeout;
223+
}
224+
}
205225
}
206226

207227
protected void putContext(SearchContext context) {
@@ -619,7 +639,7 @@ final SearchContext createContext(ShardSearchRequest request, @Nullable Engine.S
619639

620640
Engine.Searcher engineSearcher = searcher == null ? indexShard.acquireSearcher("search") : searcher;
621641

622-
SearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher);
642+
SearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher, defaultSearchTimeout);
623643
SearchContext.setCurrent(context);
624644
try {
625645
context.scroll(request.scroll());

core/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.common.lucene.MinimumScoreCollector;
3636
import org.elasticsearch.common.lucene.search.FilteredCollector;
3737
import org.elasticsearch.index.engine.Engine;
38+
import org.elasticsearch.search.SearchService;
3839
import org.elasticsearch.search.dfs.CachedDfSource;
3940
import org.elasticsearch.search.internal.SearchContext.Lifetime;
4041

@@ -139,7 +140,7 @@ public Weight createNormalizedWeight(Query query, boolean needsScores) throws IO
139140
public void search(Query query, Collector collector) throws IOException {
140141
// Wrap the caller's collector with various wrappers e.g. those used to siphon
141142
// matches off for aggregation or to impose a time-limit on collection.
142-
final boolean timeoutSet = searchContext.timeoutInMillis() != -1;
143+
final boolean timeoutSet = searchContext.timeoutInMillis() != SearchService.NO_TIMEOUT.millis();
143144
final boolean terminateAfterSet = searchContext.terminateAfter() != SearchContext.DEFAULT_TERMINATE_AFTER;
144145

145146
if (timeoutSet) {

core/src/main/java/org/elasticsearch/search/internal/DefaultSearchContext.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.common.lucene.search.Queries;
3434
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
3535
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
36+
import org.elasticsearch.common.unit.TimeValue;
3637
import org.elasticsearch.common.util.BigArrays;
3738
import org.elasticsearch.index.IndexService;
3839
import org.elasticsearch.index.analysis.AnalysisService;
@@ -90,7 +91,7 @@ public class DefaultSearchContext extends SearchContext {
9091
private ScanContext scanContext;
9192
private float queryBoost = 1.0f;
9293
// timeout in millis
93-
private long timeoutInMillis = -1;
94+
private long timeoutInMillis;
9495
// terminate after count
9596
private int terminateAfter = DEFAULT_TERMINATE_AFTER;
9697
private List<String> groupStats;
@@ -127,7 +128,9 @@ public class DefaultSearchContext extends SearchContext {
127128
public DefaultSearchContext(long id, ShardSearchRequest request, SearchShardTarget shardTarget,
128129
Engine.Searcher engineSearcher, IndexService indexService, IndexShard indexShard,
129130
ScriptService scriptService, PageCacheRecycler pageCacheRecycler,
130-
BigArrays bigArrays, Counter timeEstimateCounter, ParseFieldMatcher parseFieldMatcher) {
131+
BigArrays bigArrays, Counter timeEstimateCounter, ParseFieldMatcher parseFieldMatcher,
132+
TimeValue timeout
133+
) {
131134
super(parseFieldMatcher);
132135
this.id = id;
133136
this.request = request;
@@ -145,6 +148,7 @@ public DefaultSearchContext(long id, ShardSearchRequest request, SearchShardTarg
145148
this.indexService = indexService;
146149
this.searcher = new ContextIndexSearcher(this, engineSearcher);
147150
this.timeEstimateCounter = timeEstimateCounter;
151+
this.timeoutInMillis = timeout.millis();
148152
}
149153

150154
@Override

core/src/test/java/org/elasticsearch/test/search/MockSearchService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.indices.IndicesService;
2828
import org.elasticsearch.indices.IndicesWarmer;
2929
import org.elasticsearch.indices.cache.request.IndicesRequestCache;
30+
import org.elasticsearch.node.settings.NodeSettingsService;
3031
import org.elasticsearch.script.ScriptService;
3132
import org.elasticsearch.search.SearchService;
3233
import org.elasticsearch.search.dfs.DfsPhase;
@@ -52,10 +53,10 @@ public static void assertNoInFLightContext() {
5253
}
5354

5455
@Inject
55-
public MockSearchService(Settings settings, ClusterService clusterService, IndicesService indicesService, IndicesWarmer indicesWarmer,
56+
public MockSearchService(Settings settings, NodeSettingsService nodeSettingsService, ClusterService clusterService, IndicesService indicesService, IndicesWarmer indicesWarmer,
5657
ThreadPool threadPool, ScriptService scriptService, PageCacheRecycler pageCacheRecycler, BigArrays bigArrays,
5758
DfsPhase dfsPhase, QueryPhase queryPhase, FetchPhase fetchPhase, IndicesRequestCache indicesQueryCache) {
58-
super(settings, clusterService, indicesService, indicesWarmer, threadPool, scriptService, pageCacheRecycler, bigArrays, dfsPhase,
59+
super(settings, nodeSettingsService, clusterService, indicesService, indicesWarmer, threadPool, scriptService, pageCacheRecycler, bigArrays, dfsPhase,
5960
queryPhase, fetchPhase, indicesQueryCache);
6061
}
6162

0 commit comments

Comments
 (0)