43
43
import org .elasticsearch .index .Index ;
44
44
import org .elasticsearch .index .IndexService ;
45
45
import org .elasticsearch .index .IndexSettings ;
46
+ import org .elasticsearch .index .MergeSchedulerConfig ;
46
47
import org .elasticsearch .index .engine .Engine ;
47
48
import org .elasticsearch .index .query .InnerHitContextBuilder ;
48
49
import org .elasticsearch .index .query .MatchAllQueryBuilder ;
82
83
import org .elasticsearch .search .internal .ShardSearchRequest ;
83
84
import org .elasticsearch .search .profile .Profilers ;
84
85
import org .elasticsearch .search .query .QueryPhase ;
86
+ import org .elasticsearch .search .query .QueryPhaseExecutionException ;
85
87
import org .elasticsearch .search .query .QuerySearchRequest ;
86
88
import org .elasticsearch .search .query .QuerySearchResult ;
87
89
import org .elasticsearch .search .query .ScrollQuerySearchResult ;
106
108
import java .util .concurrent .atomic .AtomicLong ;
107
109
import java .util .function .LongSupplier ;
108
110
111
+ import static org .elasticsearch .common .unit .TimeValue .timeValueHours ;
109
112
import static org .elasticsearch .common .unit .TimeValue .timeValueMillis ;
110
113
import static org .elasticsearch .common .unit .TimeValue .timeValueMinutes ;
111
114
112
115
public class SearchService extends AbstractLifecycleComponent implements IndexEventListener {
113
116
114
117
// we can have 5 minutes here, since we make sure to clean with search requests and when shard/index closes
115
118
public static final Setting <TimeValue > DEFAULT_KEEPALIVE_SETTING =
116
- Setting .positiveTimeSetting ("search.default_keep_alive" , timeValueMinutes (5 ), Property .NodeScope );
119
+ Setting .positiveTimeSetting ("search.default_keep_alive" , timeValueMinutes (5 ), Property .NodeScope , Property .Dynamic );
120
+ public static final Setting <TimeValue > MAX_KEEPALIVE_SETTING =
121
+ Setting .positiveTimeSetting ("search.max_keep_alive" , timeValueHours (24 ), Property .NodeScope , Property .Dynamic );
117
122
public static final Setting <TimeValue > KEEPALIVE_INTERVAL_SETTING =
118
123
Setting .positiveTimeSetting ("search.keep_alive_interval" , timeValueMinutes (1 ), Property .NodeScope );
119
124
/**
@@ -147,7 +152,9 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
147
152
148
153
private final FetchPhase fetchPhase ;
149
154
150
- private final long defaultKeepAlive ;
155
+ private volatile long defaultKeepAlive ;
156
+
157
+ private volatile long maxKeepAlive ;
151
158
152
159
private volatile TimeValue defaultSearchTimeout ;
153
160
@@ -173,7 +180,10 @@ public SearchService(ClusterService clusterService, IndicesService indicesServic
173
180
this .fetchPhase = fetchPhase ;
174
181
175
182
TimeValue keepAliveInterval = KEEPALIVE_INTERVAL_SETTING .get (settings );
176
- this .defaultKeepAlive = DEFAULT_KEEPALIVE_SETTING .get (settings ).millis ();
183
+ setKeepAlives (DEFAULT_KEEPALIVE_SETTING .get (settings ), MAX_KEEPALIVE_SETTING .get (settings ));
184
+
185
+ clusterService .getClusterSettings ().addSettingsUpdateConsumer (DEFAULT_KEEPALIVE_SETTING , MAX_KEEPALIVE_SETTING ,
186
+ this ::setKeepAlives , this ::validateKeepAlives );
177
187
178
188
this .keepAliveReaper = threadPool .scheduleWithFixedDelay (new Reaper (), keepAliveInterval , Names .SAME );
179
189
@@ -184,6 +194,20 @@ public SearchService(ClusterService clusterService, IndicesService indicesServic
184
194
clusterService .getClusterSettings ().addSettingsUpdateConsumer (LOW_LEVEL_CANCELLATION_SETTING , this ::setLowLevelCancellation );
185
195
}
186
196
197
+ private void validateKeepAlives (TimeValue defaultKeepAlive , TimeValue maxKeepAlive ) {
198
+ if (defaultKeepAlive .millis () > maxKeepAlive .millis ()) {
199
+ throw new IllegalArgumentException ("Default keep alive setting for scroll [" + DEFAULT_KEEPALIVE_SETTING .getKey () + "]" +
200
+ " should be smaller than max keep alive [" + MAX_KEEPALIVE_SETTING .getKey () + "], " +
201
+ "was (" + defaultKeepAlive .format () + " > " + maxKeepAlive .format () + ")" );
202
+ }
203
+ }
204
+
205
+ private void setKeepAlives (TimeValue defaultKeepAlive , TimeValue maxKeepAlive ) {
206
+ validateKeepAlives (defaultKeepAlive , maxKeepAlive );
207
+ this .defaultKeepAlive = defaultKeepAlive .millis ();
208
+ this .maxKeepAlive = maxKeepAlive .millis ();
209
+ }
210
+
187
211
private void setDefaultSearchTimeout (TimeValue defaultSearchTimeout ) {
188
212
this .defaultSearchTimeout = defaultSearchTimeout ;
189
213
}
@@ -547,7 +571,7 @@ final SearchContext createContext(ShardSearchRequest request, @Nullable Engine.S
547
571
if (request .scroll () != null && request .scroll ().keepAlive () != null ) {
548
572
keepAlive = request .scroll ().keepAlive ().millis ();
549
573
}
550
- context . keepAlive ( keepAlive );
574
+ contextScrollKeepAlive ( context , keepAlive );
551
575
context .lowLevelCancellation (lowLevelCancellation );
552
576
} catch (Exception e ) {
553
577
context .close ();
@@ -625,6 +649,16 @@ public void freeAllScrollContexts() {
625
649
}
626
650
}
627
651
652
+ private void contextScrollKeepAlive (SearchContext context , long keepAlive ) throws IOException {
653
+ if (keepAlive > maxKeepAlive ) {
654
+ throw new QueryPhaseExecutionException (context ,
655
+ "Keep alive for scroll (" + TimeValue .timeValueMillis (keepAlive ).format () + ") is too large. " +
656
+ "It must be less than (" + TimeValue .timeValueMillis (maxKeepAlive ).format () + "). " +
657
+ "This limit can be set by changing the [" + MAX_KEEPALIVE_SETTING .getKey () + "] cluster level setting." );
658
+ }
659
+ context .keepAlive (keepAlive );
660
+ }
661
+
628
662
private void contextProcessing (SearchContext context ) {
629
663
// disable timeout while executing a search
630
664
context .accessed (-1 );
@@ -847,13 +881,13 @@ private void shortcutDocIdsToLoad(SearchContext context) {
847
881
context .docIdsToLoad (docIdsToLoad , 0 , docIdsToLoad .length );
848
882
}
849
883
850
- private void processScroll (InternalScrollSearchRequest request , SearchContext context ) {
884
+ private void processScroll (InternalScrollSearchRequest request , SearchContext context ) throws IOException {
851
885
// process scroll
852
886
context .from (context .from () + context .size ());
853
887
context .scrollContext ().scroll = request .scroll ();
854
888
// update the context keep alive based on the new scroll value
855
889
if (request .scroll () != null && request .scroll ().keepAlive () != null ) {
856
- context . keepAlive ( request .scroll ().keepAlive ().millis ());
890
+ contextScrollKeepAlive ( context , request .scroll ().keepAlive ().millis ());
857
891
}
858
892
}
859
893
0 commit comments