SearchServiceTests.java 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.search;
  20. import com.carrotsearch.hppc.IntArrayList;
  21. import org.apache.lucene.index.DirectoryReader;
  22. import org.apache.lucene.index.FilterDirectoryReader;
  23. import org.apache.lucene.index.LeafReader;
  24. import org.apache.lucene.search.Query;
  25. import org.apache.lucene.store.AlreadyClosedException;
  26. import org.elasticsearch.ElasticsearchException;
  27. import org.elasticsearch.action.ActionListener;
  28. import org.elasticsearch.action.OriginalIndices;
  29. import org.elasticsearch.action.index.IndexResponse;
  30. import org.elasticsearch.action.search.ClearScrollRequest;
  31. import org.elasticsearch.action.search.SearchPhaseExecutionException;
  32. import org.elasticsearch.action.search.SearchRequest;
  33. import org.elasticsearch.action.search.SearchResponse;
  34. import org.elasticsearch.action.search.SearchShardTask;
  35. import org.elasticsearch.action.search.SearchType;
  36. import org.elasticsearch.action.support.IndicesOptions;
  37. import org.elasticsearch.action.support.PlainActionFuture;
  38. import org.elasticsearch.action.support.WriteRequest;
  39. import org.elasticsearch.common.Strings;
  40. import org.elasticsearch.common.UUIDs;
  41. import org.elasticsearch.common.io.stream.StreamInput;
  42. import org.elasticsearch.common.io.stream.StreamOutput;
  43. import org.elasticsearch.common.settings.Settings;
  44. import org.elasticsearch.common.unit.TimeValue;
  45. import org.elasticsearch.common.xcontent.XContentBuilder;
  46. import org.elasticsearch.index.Index;
  47. import org.elasticsearch.index.IndexModule;
  48. import org.elasticsearch.index.IndexService;
  49. import org.elasticsearch.index.IndexSettings;
  50. import org.elasticsearch.index.query.AbstractQueryBuilder;
  51. import org.elasticsearch.index.query.MatchAllQueryBuilder;
  52. import org.elasticsearch.index.query.MatchNoneQueryBuilder;
  53. import org.elasticsearch.index.query.QueryBuilder;
  54. import org.elasticsearch.index.query.QueryRewriteContext;
  55. import org.elasticsearch.index.query.QueryShardContext;
  56. import org.elasticsearch.index.query.TermQueryBuilder;
  57. import org.elasticsearch.index.search.stats.SearchStats;
  58. import org.elasticsearch.index.shard.IndexShard;
  59. import org.elasticsearch.index.shard.SearchOperationListener;
  60. import org.elasticsearch.index.shard.ShardId;
  61. import org.elasticsearch.indices.IndicesService;
  62. import org.elasticsearch.indices.settings.InternalOrPrivateSettingsPlugin;
  63. import org.elasticsearch.plugins.Plugin;
  64. import org.elasticsearch.plugins.SearchPlugin;
  65. import org.elasticsearch.script.MockScriptEngine;
  66. import org.elasticsearch.script.MockScriptPlugin;
  67. import org.elasticsearch.script.Script;
  68. import org.elasticsearch.script.ScriptType;
  69. import org.elasticsearch.search.aggregations.AggregationBuilders;
  70. import org.elasticsearch.search.aggregations.InternalAggregation;
  71. import org.elasticsearch.search.aggregations.MultiBucketConsumerService;
  72. import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder;
  73. import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
  74. import org.elasticsearch.search.aggregations.support.ValueType;
  75. import org.elasticsearch.search.builder.SearchSourceBuilder;
  76. import org.elasticsearch.search.fetch.FetchSearchResult;
  77. import org.elasticsearch.search.fetch.ShardFetchRequest;
  78. import org.elasticsearch.search.internal.AliasFilter;
  79. import org.elasticsearch.search.internal.SearchContext;
  80. import org.elasticsearch.search.internal.SearchContextId;
  81. import org.elasticsearch.search.internal.ShardSearchRequest;
  82. import org.elasticsearch.search.query.QuerySearchResult;
  83. import org.elasticsearch.search.suggest.SuggestBuilder;
  84. import org.elasticsearch.test.ESSingleNodeTestCase;
  85. import org.junit.Before;
  86. import java.io.IOException;
  87. import java.util.ArrayList;
  88. import java.util.Collection;
  89. import java.util.Collections;
  90. import java.util.LinkedList;
  91. import java.util.List;
  92. import java.util.Locale;
  93. import java.util.Map;
  94. import java.util.concurrent.CountDownLatch;
  95. import java.util.concurrent.ExecutionException;
  96. import java.util.concurrent.Semaphore;
  97. import java.util.concurrent.atomic.AtomicBoolean;
  98. import java.util.concurrent.atomic.AtomicInteger;
  99. import java.util.function.Function;
  100. import static java.util.Collections.singletonList;
  101. import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
  102. import static org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason.DELETED;
  103. import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
  104. import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
  105. import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
  106. import static org.hamcrest.CoreMatchers.equalTo;
  107. import static org.hamcrest.CoreMatchers.instanceOf;
  108. import static org.hamcrest.CoreMatchers.is;
  109. import static org.hamcrest.CoreMatchers.notNullValue;
  110. import static org.hamcrest.CoreMatchers.startsWith;
  111. public class SearchServiceTests extends ESSingleNodeTestCase {
  112. @Override
  113. protected boolean resetNodeAfterTest() {
  114. return true;
  115. }
  116. @Override
  117. protected Collection<Class<? extends Plugin>> getPlugins() {
  118. return pluginList(FailOnRewriteQueryPlugin.class, CustomScriptPlugin.class,
  119. ReaderWrapperCountPlugin.class, InternalOrPrivateSettingsPlugin.class);
  120. }
  121. public static class ReaderWrapperCountPlugin extends Plugin {
  122. @Override
  123. public void onIndexModule(IndexModule indexModule) {
  124. indexModule.setReaderWrapper(service -> SearchServiceTests::apply);
  125. }
  126. }
  127. @Before
  128. private void resetCount() {
  129. numWrapInvocations = new AtomicInteger(0);
  130. }
  131. private static AtomicInteger numWrapInvocations = new AtomicInteger(0);
  132. private static DirectoryReader apply(DirectoryReader directoryReader) throws IOException {
  133. numWrapInvocations.incrementAndGet();
  134. return new FilterDirectoryReader(directoryReader,
  135. new FilterDirectoryReader.SubReaderWrapper() {
  136. @Override
  137. public LeafReader wrap(LeafReader reader) {
  138. return reader;
  139. }
  140. }) {
  141. @Override
  142. protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) throws IOException {
  143. return in;
  144. }
  145. @Override
  146. public CacheHelper getReaderCacheHelper() {
  147. return directoryReader.getReaderCacheHelper();
  148. }
  149. };
  150. }
  151. public static class CustomScriptPlugin extends MockScriptPlugin {
  152. static final String DUMMY_SCRIPT = "dummyScript";
  153. @Override
  154. protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {
  155. return Collections.singletonMap(DUMMY_SCRIPT, vars -> "dummy");
  156. }
  157. @Override
  158. public void onIndexModule(IndexModule indexModule) {
  159. indexModule.addSearchOperationListener(new SearchOperationListener() {
  160. @Override
  161. public void onNewContext(SearchContext context) {
  162. if (context.query() != null) {
  163. if ("throttled_threadpool_index".equals(context.indexShard().shardId().getIndex().getName())) {
  164. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search_throttled]"));
  165. } else {
  166. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search]"));
  167. }
  168. }
  169. }
  170. @Override
  171. public void onFetchPhase(SearchContext context, long tookInNanos) {
  172. if ("throttled_threadpool_index".equals(context.indexShard().shardId().getIndex().getName())) {
  173. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search_throttled]"));
  174. } else {
  175. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search]"));
  176. }
  177. }
  178. @Override
  179. public void onQueryPhase(SearchContext context, long tookInNanos) {
  180. if ("throttled_threadpool_index".equals(context.indexShard().shardId().getIndex().getName())) {
  181. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search_throttled]"));
  182. } else {
  183. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search]"));
  184. }
  185. }
  186. });
  187. }
  188. }
  189. @Override
  190. protected Settings nodeSettings() {
  191. return Settings.builder().put("search.default_search_timeout", "5s").build();
  192. }
  193. public void testClearOnClose() {
  194. createIndex("index");
  195. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  196. SearchResponse searchResponse = client().prepareSearch("index").setSize(1).setScroll("1m").get();
  197. assertThat(searchResponse.getScrollId(), is(notNullValue()));
  198. SearchService service = getInstanceFromNode(SearchService.class);
  199. assertEquals(1, service.getActiveContexts());
  200. service.doClose(); // this kills the keep-alive reaper we have to reset the node after this test
  201. assertEquals(0, service.getActiveContexts());
  202. }
  203. public void testClearOnStop() {
  204. createIndex("index");
  205. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  206. SearchResponse searchResponse = client().prepareSearch("index").setSize(1).setScroll("1m").get();
  207. assertThat(searchResponse.getScrollId(), is(notNullValue()));
  208. SearchService service = getInstanceFromNode(SearchService.class);
  209. assertEquals(1, service.getActiveContexts());
  210. service.doStop();
  211. assertEquals(0, service.getActiveContexts());
  212. }
  213. public void testClearIndexDelete() {
  214. createIndex("index");
  215. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  216. SearchResponse searchResponse = client().prepareSearch("index").setSize(1).setScroll("1m").get();
  217. assertThat(searchResponse.getScrollId(), is(notNullValue()));
  218. SearchService service = getInstanceFromNode(SearchService.class);
  219. assertEquals(1, service.getActiveContexts());
  220. assertAcked(client().admin().indices().prepareDelete("index"));
  221. assertEquals(0, service.getActiveContexts());
  222. }
  223. public void testCloseSearchContextOnRewriteException() {
  224. // if refresh happens while checking the exception, the subsequent reference count might not match, so we switch it off
  225. createIndex("index", Settings.builder().put("index.refresh_interval", -1).build());
  226. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  227. SearchService service = getInstanceFromNode(SearchService.class);
  228. IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  229. IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  230. IndexShard indexShard = indexService.getShard(0);
  231. final int activeContexts = service.getActiveContexts();
  232. final int activeRefs = indexShard.store().refCount();
  233. expectThrows(SearchPhaseExecutionException.class, () ->
  234. client().prepareSearch("index").setQuery(new FailOnRewriteQueryBuilder()).get());
  235. assertEquals(activeContexts, service.getActiveContexts());
  236. assertEquals(activeRefs, indexShard.store().refCount());
  237. }
  238. public void testSearchWhileIndexDeleted() throws InterruptedException {
  239. createIndex("index");
  240. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  241. SearchService service = getInstanceFromNode(SearchService.class);
  242. IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  243. IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  244. IndexShard indexShard = indexService.getShard(0);
  245. AtomicBoolean running = new AtomicBoolean(true);
  246. CountDownLatch startGun = new CountDownLatch(1);
  247. Semaphore semaphore = new Semaphore(Integer.MAX_VALUE);
  248. final Thread thread = new Thread() {
  249. @Override
  250. public void run() {
  251. startGun.countDown();
  252. while(running.get()) {
  253. service.afterIndexRemoved(indexService.index(), indexService.getIndexSettings(), DELETED);
  254. if (randomBoolean()) {
  255. // here we trigger some refreshes to ensure the IR go out of scope such that we hit ACE if we access a search
  256. // context in a non-sane way.
  257. try {
  258. semaphore.acquire();
  259. } catch (InterruptedException e) {
  260. throw new AssertionError(e);
  261. }
  262. client().prepareIndex("index").setSource("field", "value")
  263. .setRefreshPolicy(randomFrom(WriteRequest.RefreshPolicy.values())).execute(new ActionListener<IndexResponse>() {
  264. @Override
  265. public void onResponse(IndexResponse indexResponse) {
  266. semaphore.release();
  267. }
  268. @Override
  269. public void onFailure(Exception e) {
  270. semaphore.release();
  271. }
  272. });
  273. }
  274. }
  275. }
  276. };
  277. thread.start();
  278. startGun.await();
  279. try {
  280. final int rounds = scaledRandomIntBetween(100, 10000);
  281. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  282. SearchRequest scrollSearchRequest = new SearchRequest().allowPartialSearchResults(true)
  283. .scroll(new Scroll(TimeValue.timeValueMinutes(1)));
  284. for (int i = 0; i < rounds; i++) {
  285. try {
  286. try {
  287. PlainActionFuture<SearchPhaseResult> result = new PlainActionFuture<>();
  288. final boolean useScroll = randomBoolean();
  289. service.executeQueryPhase(
  290. new ShardSearchRequest(OriginalIndices.NONE, useScroll ? scrollSearchRequest : searchRequest,
  291. indexShard.shardId(), 1,
  292. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null),
  293. new SearchShardTask(123L, "", "", "", null, Collections.emptyMap()), result);
  294. SearchPhaseResult searchPhaseResult = result.get();
  295. IntArrayList intCursors = new IntArrayList(1);
  296. intCursors.add(0);
  297. ShardFetchRequest req = new ShardFetchRequest(searchPhaseResult.getContextId(), intCursors, null/* not a scroll */);
  298. PlainActionFuture<FetchSearchResult> listener = new PlainActionFuture<>();
  299. service.executeFetchPhase(req, new SearchShardTask(123L, "", "", "", null, Collections.emptyMap()), listener);
  300. listener.get();
  301. if (useScroll) {
  302. service.freeContext(searchPhaseResult.getContextId());
  303. }
  304. } catch (ExecutionException ex) {
  305. assertThat(ex.getCause(), instanceOf(RuntimeException.class));
  306. throw ((RuntimeException)ex.getCause());
  307. }
  308. } catch (AlreadyClosedException ex) {
  309. throw ex;
  310. } catch (IllegalStateException ex) {
  311. assertEquals("search context is already closed can't increment refCount current count [0]", ex.getMessage());
  312. } catch (SearchContextMissingException ex) {
  313. // that's fine
  314. }
  315. }
  316. } finally {
  317. running.set(false);
  318. thread.join();
  319. semaphore.acquire(Integer.MAX_VALUE);
  320. }
  321. assertEquals(0, service.getActiveContexts());
  322. SearchStats.Stats totalStats = indexShard.searchStats().getTotal();
  323. assertEquals(0, totalStats.getQueryCurrent());
  324. assertEquals(0, totalStats.getScrollCurrent());
  325. assertEquals(0, totalStats.getFetchCurrent());
  326. }
  327. public void testTimeout() throws IOException {
  328. createIndex("index");
  329. final SearchService service = getInstanceFromNode(SearchService.class);
  330. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  331. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  332. final IndexShard indexShard = indexService.getShard(0);
  333. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  334. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(
  335. new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  336. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null),
  337. indexShard);
  338. final SearchContext contextWithDefaultTimeout = service.createContext(rewriteContext);
  339. try {
  340. // the search context should inherit the default timeout
  341. assertThat(contextWithDefaultTimeout.timeout(), equalTo(TimeValue.timeValueSeconds(5)));
  342. } finally {
  343. contextWithDefaultTimeout.decRef();
  344. service.freeContext(contextWithDefaultTimeout.id());
  345. }
  346. final long seconds = randomIntBetween(6, 10);
  347. searchRequest.source(new SearchSourceBuilder().timeout(TimeValue.timeValueSeconds(seconds)));
  348. rewriteContext = service.acquireSearcherAndRewrite(
  349. new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  350. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null),
  351. indexShard);
  352. final SearchContext context = service.createContext(rewriteContext);
  353. try {
  354. // the search context should inherit the query timeout
  355. assertThat(context.timeout(), equalTo(TimeValue.timeValueSeconds(seconds)));
  356. } finally {
  357. context.decRef();
  358. service.freeContext(context.id());
  359. }
  360. }
  361. /**
  362. * test that getting more than the allowed number of docvalue_fields throws an exception
  363. */
  364. public void testMaxDocvalueFieldsSearch() throws IOException {
  365. createIndex("index");
  366. final SearchService service = getInstanceFromNode(SearchService.class);
  367. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  368. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  369. final IndexShard indexShard = indexService.getShard(0);
  370. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  371. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  372. searchRequest.source(searchSourceBuilder);
  373. // adding the maximum allowed number of docvalue_fields to retrieve
  374. for (int i = 0; i < indexService.getIndexSettings().getMaxDocvalueFields(); i++) {
  375. searchSourceBuilder.docValueField("field" + i);
  376. }
  377. ShardSearchRequest shardRequest = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  378. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null);
  379. {
  380. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(shardRequest, indexShard);
  381. try (SearchContext context = service.createContext(rewriteContext)) {
  382. assertNotNull(context);
  383. }
  384. }
  385. {
  386. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(shardRequest, indexShard);
  387. searchSourceBuilder.docValueField("one_field_too_much");
  388. IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
  389. () -> service.createContext(rewriteContext));
  390. assertEquals(
  391. "Trying to retrieve too many docvalue_fields. Must be less than or equal to: [100] but was [101]. "
  392. + "This limit can be set by changing the [index.max_docvalue_fields_search] index level setting.", ex.getMessage());
  393. }
  394. }
  395. /**
  396. * test that getting more than the allowed number of script_fields throws an exception
  397. */
  398. public void testMaxScriptFieldsSearch() throws IOException {
  399. createIndex("index");
  400. final SearchService service = getInstanceFromNode(SearchService.class);
  401. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  402. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  403. final IndexShard indexShard = indexService.getShard(0);
  404. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  405. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  406. searchRequest.source(searchSourceBuilder);
  407. // adding the maximum allowed number of script_fields to retrieve
  408. int maxScriptFields = indexService.getIndexSettings().getMaxScriptFields();
  409. for (int i = 0; i < maxScriptFields; i++) {
  410. searchSourceBuilder.scriptField("field" + i,
  411. new Script(ScriptType.INLINE, MockScriptEngine.NAME, CustomScriptPlugin.DUMMY_SCRIPT, Collections.emptyMap()));
  412. }
  413. ShardSearchRequest shardRequest = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  414. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null);
  415. {
  416. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(shardRequest, indexShard);
  417. try (SearchContext context = service.createContext(rewriteContext)) {
  418. assertNotNull(context);
  419. }
  420. }
  421. {
  422. searchSourceBuilder.scriptField("anotherScriptField",
  423. new Script(ScriptType.INLINE, MockScriptEngine.NAME, CustomScriptPlugin.DUMMY_SCRIPT, Collections.emptyMap()));
  424. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(shardRequest, indexShard);
  425. IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
  426. () -> service.createContext(rewriteContext));
  427. assertEquals(
  428. "Trying to retrieve too many script_fields. Must be less than or equal to: [" + maxScriptFields + "] but was ["
  429. + (maxScriptFields + 1)
  430. + "]. This limit can be set by changing the [index.max_script_fields] index level setting.",
  431. ex.getMessage());
  432. }
  433. }
  434. public void testIgnoreScriptfieldIfSizeZero() throws IOException {
  435. createIndex("index");
  436. final SearchService service = getInstanceFromNode(SearchService.class);
  437. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  438. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  439. final IndexShard indexShard = indexService.getShard(0);
  440. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  441. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  442. searchRequest.source(searchSourceBuilder);
  443. searchSourceBuilder.scriptField("field" + 0,
  444. new Script(ScriptType.INLINE, MockScriptEngine.NAME, CustomScriptPlugin.DUMMY_SCRIPT, Collections.emptyMap()));
  445. searchSourceBuilder.size(0);
  446. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(
  447. new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  448. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null),
  449. indexShard);
  450. try (SearchContext context = service.createContext(rewriteContext)) {
  451. assertEquals(0, context.scriptFields().fields().size());
  452. }
  453. }
  454. /**
  455. * test that creating more than the allowed number of scroll contexts throws an exception
  456. */
  457. public void testMaxOpenScrollContexts() throws RuntimeException, IOException {
  458. createIndex("index");
  459. client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  460. final SearchService service = getInstanceFromNode(SearchService.class);
  461. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  462. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  463. final IndexShard indexShard = indexService.getShard(0);
  464. // Open all possible scrolls, clear some of them, then open more until the limit is reached
  465. LinkedList<String> clearScrollIds = new LinkedList<>();
  466. for (int i = 0; i < SearchService.MAX_OPEN_SCROLL_CONTEXT.get(Settings.EMPTY); i++) {
  467. SearchResponse searchResponse = client().prepareSearch("index").setSize(1).setScroll("1m").get();
  468. if (randomInt(4) == 0) clearScrollIds.addLast(searchResponse.getScrollId());
  469. }
  470. ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
  471. clearScrollRequest.setScrollIds(clearScrollIds);
  472. client().clearScroll(clearScrollRequest);
  473. for (int i = 0; i < clearScrollIds.size(); i++) {
  474. client().prepareSearch("index").setSize(1).setScroll("1m").get();
  475. }
  476. SearchService.SearchRewriteContext rewriteContext =
  477. service.acquireSearcherAndRewrite(new ShardScrollRequestTest(indexShard.shardId()), indexShard);
  478. ElasticsearchException ex = expectThrows(ElasticsearchException.class,
  479. () -> service.createAndPutContext(rewriteContext));
  480. assertEquals(
  481. "Trying to create too many scroll contexts. Must be less than or equal to: [" +
  482. SearchService.MAX_OPEN_SCROLL_CONTEXT.get(Settings.EMPTY) + "]. " +
  483. "This limit can be set by changing the [search.max_open_scroll_context] setting.",
  484. ex.getMessage());
  485. }
  486. public void testOpenScrollContextsConcurrently() throws Exception {
  487. createIndex("index");
  488. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  489. final IndexShard indexShard = indicesService.indexServiceSafe(resolveIndex("index")).getShard(0);
  490. final int maxScrollContexts = SearchService.MAX_OPEN_SCROLL_CONTEXT.get(Settings.EMPTY);
  491. final SearchService searchService = getInstanceFromNode(SearchService.class);
  492. Thread[] threads = new Thread[randomIntBetween(2, 8)];
  493. CountDownLatch latch = new CountDownLatch(threads.length);
  494. for (int i = 0; i < threads.length; i++) {
  495. threads[i] = new Thread(() -> {
  496. latch.countDown();
  497. try {
  498. latch.await();
  499. for (; ; ) {
  500. SearchService.SearchRewriteContext rewriteContext =
  501. searchService.acquireSearcherAndRewrite(new ShardScrollRequestTest(indexShard.shardId()), indexShard);
  502. try {
  503. searchService.createAndPutContext(rewriteContext);
  504. } catch (ElasticsearchException e) {
  505. assertThat(e.getMessage(), equalTo(
  506. "Trying to create too many scroll contexts. Must be less than or equal to: " +
  507. "[" + maxScrollContexts + "]. " +
  508. "This limit can be set by changing the [search.max_open_scroll_context] setting."));
  509. return;
  510. }
  511. }
  512. } catch (Exception e) {
  513. throw new AssertionError(e);
  514. }
  515. });
  516. threads[i].setName("elasticsearch[node_s_0][search]");
  517. threads[i].start();
  518. }
  519. for (Thread thread : threads) {
  520. thread.join();
  521. }
  522. assertThat(searchService.getActiveContexts(), equalTo(maxScrollContexts));
  523. searchService.freeAllScrollContexts();
  524. }
  525. public static class FailOnRewriteQueryPlugin extends Plugin implements SearchPlugin {
  526. @Override
  527. public List<QuerySpec<?>> getQueries() {
  528. return singletonList(new QuerySpec<>("fail_on_rewrite_query", FailOnRewriteQueryBuilder::new, parseContext -> {
  529. throw new UnsupportedOperationException("No query parser for this plugin");
  530. }));
  531. }
  532. }
  533. public static class FailOnRewriteQueryBuilder extends AbstractQueryBuilder<FailOnRewriteQueryBuilder> {
  534. public FailOnRewriteQueryBuilder(StreamInput in) throws IOException {
  535. super(in);
  536. }
  537. public FailOnRewriteQueryBuilder() {
  538. }
  539. @Override
  540. protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) {
  541. if (queryRewriteContext.convertToShardContext() != null) {
  542. throw new IllegalStateException("Fail on rewrite phase");
  543. }
  544. return this;
  545. }
  546. @Override
  547. protected void doWriteTo(StreamOutput out) {
  548. }
  549. @Override
  550. protected void doXContent(XContentBuilder builder, Params params) {
  551. }
  552. @Override
  553. protected Query doToQuery(QueryShardContext context) {
  554. return null;
  555. }
  556. @Override
  557. protected boolean doEquals(FailOnRewriteQueryBuilder other) {
  558. return false;
  559. }
  560. @Override
  561. protected int doHashCode() {
  562. return 0;
  563. }
  564. @Override
  565. public String getWriteableName() {
  566. return null;
  567. }
  568. }
  569. private static class ShardScrollRequestTest extends ShardSearchRequest {
  570. private Scroll scroll;
  571. ShardScrollRequestTest(ShardId shardId) {
  572. super(OriginalIndices.NONE, new SearchRequest().allowPartialSearchResults(true),
  573. shardId, 1, new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null);
  574. this.scroll = new Scroll(TimeValue.timeValueMinutes(1));
  575. }
  576. @Override
  577. public Scroll scroll() {
  578. return this.scroll;
  579. }
  580. }
  581. public void testCanMatch() throws IOException, InterruptedException {
  582. createIndex("index");
  583. final SearchService service = getInstanceFromNode(SearchService.class);
  584. final IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  585. final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  586. final IndexShard indexShard = indexService.getShard(0);
  587. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true);
  588. int numWrapReader = numWrapInvocations.get();
  589. assertTrue(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  590. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  591. searchRequest.source(new SearchSourceBuilder());
  592. assertTrue(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  593. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  594. searchRequest.source(new SearchSourceBuilder().query(new MatchAllQueryBuilder()));
  595. assertTrue(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  596. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  597. searchRequest.source(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  598. .aggregation(new TermsAggregationBuilder("test", ValueType.STRING).minDocCount(0)));
  599. assertTrue(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  600. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  601. searchRequest.source(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  602. .aggregation(new GlobalAggregationBuilder("test")));
  603. assertTrue(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  604. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  605. searchRequest.source(new SearchSourceBuilder().query(new MatchNoneQueryBuilder()));
  606. assertFalse(service.canMatch(new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  607. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null)).canMatch());
  608. assertEquals(numWrapReader, numWrapInvocations.get());
  609. ShardSearchRequest request = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(), 1,
  610. new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null);
  611. CountDownLatch latch = new CountDownLatch(1);
  612. SearchShardTask task = new SearchShardTask(123L, "", "", "", null, Collections.emptyMap());
  613. service.executeQueryPhase(request, task, new ActionListener<SearchPhaseResult>() {
  614. @Override
  615. public void onResponse(SearchPhaseResult searchPhaseResult) {
  616. try {
  617. // make sure that the wrapper is called when the query is actually executed
  618. assertEquals(numWrapReader+1, numWrapInvocations.get());
  619. } finally {
  620. latch.countDown();
  621. }
  622. }
  623. @Override
  624. public void onFailure(Exception e) {
  625. try {
  626. throw new AssertionError(e);
  627. } finally {
  628. latch.countDown();
  629. }
  630. }
  631. });
  632. latch.await();
  633. }
  634. public void testCanRewriteToMatchNone() {
  635. assertFalse(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  636. .aggregation(new GlobalAggregationBuilder("test"))));
  637. assertFalse(SearchService.canRewriteToMatchNone(new SearchSourceBuilder()));
  638. assertFalse(SearchService.canRewriteToMatchNone(null));
  639. assertFalse(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  640. .aggregation(new TermsAggregationBuilder("test", ValueType.STRING).minDocCount(0))));
  641. assertTrue(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new TermQueryBuilder("foo", "bar"))));
  642. assertTrue(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  643. .aggregation(new TermsAggregationBuilder("test", ValueType.STRING).minDocCount(1))));
  644. assertFalse(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new MatchNoneQueryBuilder())
  645. .aggregation(new TermsAggregationBuilder("test", ValueType.STRING).minDocCount(1))
  646. .suggest(new SuggestBuilder())));
  647. assertFalse(SearchService.canRewriteToMatchNone(new SearchSourceBuilder().query(new TermQueryBuilder("foo", "bar"))
  648. .suggest(new SuggestBuilder())));
  649. }
  650. public void testSetSearchThrottled() {
  651. createIndex("throttled_threadpool_index");
  652. client().execute(
  653. InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.INSTANCE,
  654. new InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.Request("throttled_threadpool_index",
  655. IndexSettings.INDEX_SEARCH_THROTTLED.getKey(), "true"))
  656. .actionGet();
  657. final SearchService service = getInstanceFromNode(SearchService.class);
  658. Index index = resolveIndex("throttled_threadpool_index");
  659. assertTrue(service.getIndicesService().indexServiceSafe(index).getIndexSettings().isSearchThrottled());
  660. client().prepareIndex("throttled_threadpool_index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  661. SearchResponse searchResponse = client().prepareSearch("throttled_threadpool_index")
  662. .setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_FORBID_CLOSED).setSize(1).get();
  663. assertSearchHits(searchResponse, "1");
  664. // we add a search action listener in a plugin above to assert that this is actually used
  665. client().execute(
  666. InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.INSTANCE,
  667. new InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.Request("throttled_threadpool_index",
  668. IndexSettings.INDEX_SEARCH_THROTTLED.getKey(), "false"))
  669. .actionGet();
  670. IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () ->
  671. client().admin().indices().prepareUpdateSettings("throttled_threadpool_index").setSettings(Settings.builder().put(IndexSettings
  672. .INDEX_SEARCH_THROTTLED.getKey(), false)).get());
  673. assertEquals("can not update private setting [index.search.throttled]; this setting is managed by Elasticsearch",
  674. iae.getMessage());
  675. assertFalse(service.getIndicesService().indexServiceSafe(index).getIndexSettings().isSearchThrottled());
  676. SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(false);
  677. ShardSearchRequest req = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, new ShardId(index, 0), 1,
  678. new AliasFilter(null, Strings.EMPTY_ARRAY), 1f, -1, null, null);
  679. Thread currentThread = Thread.currentThread();
  680. // we still make sure can match is executed on the network thread
  681. service.canMatch(req, ActionListener.wrap(r -> assertSame(Thread.currentThread(), currentThread), e -> fail("unexpected")));
  682. }
  683. public void testExpandSearchThrottled() {
  684. createIndex("throttled_threadpool_index");
  685. client().execute(
  686. InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.INSTANCE,
  687. new InternalOrPrivateSettingsPlugin.UpdateInternalOrPrivateAction.Request("throttled_threadpool_index",
  688. IndexSettings.INDEX_SEARCH_THROTTLED.getKey(), "true"))
  689. .actionGet();
  690. client().prepareIndex("throttled_threadpool_index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get();
  691. assertHitCount(client().prepareSearch().get(), 0L);
  692. assertHitCount(client().prepareSearch().setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_FORBID_CLOSED).get(), 1L);
  693. }
  694. public void testCreateReduceContext() {
  695. final SearchService service = getInstanceFromNode(SearchService.class);
  696. {
  697. InternalAggregation.ReduceContext reduceContext = service.createReduceContext(true);
  698. expectThrows(MultiBucketConsumerService.TooManyBucketsException.class,
  699. () -> reduceContext.consumeBucketsAndMaybeBreak(MultiBucketConsumerService.DEFAULT_MAX_BUCKETS + 1));
  700. }
  701. {
  702. InternalAggregation.ReduceContext reduceContext = service.createReduceContext(false);
  703. reduceContext.consumeBucketsAndMaybeBreak(MultiBucketConsumerService.DEFAULT_MAX_BUCKETS + 1);
  704. }
  705. }
  706. public void testCreateSearchContext() throws IOException {
  707. String index = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
  708. IndexService indexService = createIndex(index);
  709. final SearchService service = getInstanceFromNode(SearchService.class);
  710. ShardId shardId = new ShardId(indexService.index(), 0);
  711. long nowInMillis = System.currentTimeMillis();
  712. String clusterAlias = randomBoolean() ? null : randomAlphaOfLengthBetween(3, 10);
  713. SearchRequest searchRequest = new SearchRequest();
  714. searchRequest.allowPartialSearchResults(randomBoolean());
  715. ShardSearchRequest request = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, shardId,
  716. indexService.numberOfShards(), AliasFilter.EMPTY, 1f, nowInMillis, clusterAlias, Strings.EMPTY_ARRAY);
  717. try (DefaultSearchContext searchContext = service.createSearchContext(request, new TimeValue(System.currentTimeMillis()))) {
  718. SearchShardTarget searchShardTarget = searchContext.shardTarget();
  719. QueryShardContext queryShardContext = searchContext.getQueryShardContext();
  720. String expectedIndexName = clusterAlias == null ? index : clusterAlias + ":" + index;
  721. assertEquals(expectedIndexName, queryShardContext.getFullyQualifiedIndex().getName());
  722. assertEquals(expectedIndexName, searchShardTarget.getFullyQualifiedIndexName());
  723. assertEquals(clusterAlias, searchShardTarget.getClusterAlias());
  724. assertEquals(shardId, searchShardTarget.getShardId());
  725. assertSame(searchShardTarget, searchContext.dfsResult().getSearchShardTarget());
  726. assertSame(searchShardTarget, searchContext.queryResult().getSearchShardTarget());
  727. assertSame(searchShardTarget, searchContext.fetchResult().getSearchShardTarget());
  728. }
  729. }
  730. /**
  731. * While we have no NPE in DefaultContext constructor anymore, we still want to guard against it (or other failures) in the future to
  732. * avoid leaking searchers.
  733. */
  734. public void testCreateSearchContextFailure() throws IOException {
  735. final String index = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
  736. final IndexService indexService = createIndex(index);
  737. final SearchService service = getInstanceFromNode(SearchService.class);
  738. final ShardId shardId = new ShardId(indexService.index(), 0);
  739. IndexShard indexShard = indexService.getShard(0);
  740. SearchService.SearchRewriteContext rewriteContext = service.acquireSearcherAndRewrite(
  741. new ShardSearchRequest(shardId, 0, AliasFilter.EMPTY) {
  742. @Override
  743. public SearchType searchType() {
  744. // induce an artificial NPE
  745. throw new NullPointerException("expected");
  746. }
  747. }, indexShard);
  748. NullPointerException e = expectThrows(NullPointerException.class,
  749. () -> service.createContext(rewriteContext));
  750. assertEquals("expected", e.getMessage());
  751. assertEquals("should have 2 store refs (IndexService + InternalEngine)", 2, indexService.getShard(0).store().refCount());
  752. }
  753. public void testMatchNoDocsEmptyResponse() throws InterruptedException {
  754. createIndex("index");
  755. Thread currentThread = Thread.currentThread();
  756. SearchService service = getInstanceFromNode(SearchService.class);
  757. IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  758. IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  759. IndexShard indexShard = indexService.getShard(0);
  760. SearchRequest searchRequest = new SearchRequest()
  761. .allowPartialSearchResults(false)
  762. .source(new SearchSourceBuilder()
  763. .aggregation(AggregationBuilders.count("count").field("value")));
  764. ShardSearchRequest shardRequest = new ShardSearchRequest(OriginalIndices.NONE, searchRequest, indexShard.shardId(),
  765. 5, AliasFilter.EMPTY, 1.0f, 0, null, null);
  766. SearchShardTask task = new SearchShardTask(123L, "", "", "", null, Collections.emptyMap());
  767. {
  768. CountDownLatch latch = new CountDownLatch(1);
  769. shardRequest.source().query(new MatchAllQueryBuilder());
  770. service.executeQueryPhase(shardRequest, task, new ActionListener<>() {
  771. @Override
  772. public void onResponse(SearchPhaseResult result) {
  773. try {
  774. assertNotSame(Thread.currentThread(), currentThread);
  775. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search]"));
  776. assertThat(result, instanceOf(QuerySearchResult.class));
  777. assertFalse(result.queryResult().isNull());
  778. assertNotNull(result.queryResult().topDocs());
  779. assertNotNull(result.queryResult().aggregations());
  780. } finally {
  781. latch.countDown();
  782. }
  783. }
  784. @Override
  785. public void onFailure(Exception exc) {
  786. try {
  787. throw new AssertionError(exc);
  788. } finally {
  789. latch.countDown();
  790. }
  791. }
  792. });
  793. latch.await();
  794. }
  795. {
  796. CountDownLatch latch = new CountDownLatch(1);
  797. shardRequest.source().query(new MatchNoneQueryBuilder());
  798. service.executeQueryPhase(shardRequest, task, new ActionListener<>() {
  799. @Override
  800. public void onResponse(SearchPhaseResult result) {
  801. try {
  802. assertNotSame(Thread.currentThread(), currentThread);
  803. assertThat(Thread.currentThread().getName(), startsWith("elasticsearch[node_s_0][search]"));
  804. assertThat(result, instanceOf(QuerySearchResult.class));
  805. assertFalse(result.queryResult().isNull());
  806. assertNotNull(result.queryResult().topDocs());
  807. assertNotNull(result.queryResult().aggregations());
  808. } finally {
  809. latch.countDown();
  810. }
  811. }
  812. @Override
  813. public void onFailure(Exception exc) {
  814. try {
  815. throw new AssertionError(exc);
  816. } finally {
  817. latch.countDown();
  818. }
  819. }
  820. });
  821. latch.await();
  822. }
  823. {
  824. CountDownLatch latch = new CountDownLatch(1);
  825. shardRequest.canReturnNullResponseIfMatchNoDocs(true);
  826. service.executeQueryPhase(shardRequest, task, new ActionListener<>() {
  827. @Override
  828. public void onResponse(SearchPhaseResult result) {
  829. try {
  830. // make sure we don't use the search threadpool
  831. assertSame(Thread.currentThread(), currentThread);
  832. assertThat(result, instanceOf(QuerySearchResult.class));
  833. assertTrue(result.queryResult().isNull());
  834. } finally {
  835. latch.countDown();
  836. }
  837. }
  838. @Override
  839. public void onFailure(Exception e) {
  840. try {
  841. throw new AssertionError(e);
  842. } finally {
  843. latch.countDown();
  844. }
  845. }
  846. });
  847. latch.await();
  848. }
  849. }
  850. public void testDeleteIndexWhileSearch() throws Exception {
  851. createIndex("test");
  852. int numDocs = randomIntBetween(1, 20);
  853. for (int i = 0; i < numDocs; i++) {
  854. client().prepareIndex("test").setSource("f", "v").get();
  855. }
  856. client().admin().indices().prepareRefresh("test").get();
  857. AtomicBoolean stopped = new AtomicBoolean(false);
  858. Thread[] searchers = new Thread[randomIntBetween(1, 4)];
  859. CountDownLatch latch = new CountDownLatch(searchers.length);
  860. for (int i = 0; i < searchers.length; i++) {
  861. searchers[i] = new Thread(() -> {
  862. latch.countDown();
  863. while (stopped.get() == false) {
  864. try {
  865. client().prepareSearch("test").setRequestCache(false).get();
  866. } catch (Exception ignored) {
  867. return;
  868. }
  869. }
  870. });
  871. searchers[i].start();
  872. }
  873. latch.await();
  874. client().admin().indices().prepareDelete("test").get();
  875. stopped.set(true);
  876. for (Thread searcher : searchers) {
  877. searcher.join();
  878. }
  879. }
  880. public void testLookUpSearchContext() throws Exception {
  881. createIndex("index");
  882. SearchService searchService = getInstanceFromNode(SearchService.class);
  883. IndicesService indicesService = getInstanceFromNode(IndicesService.class);
  884. IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index"));
  885. IndexShard indexShard = indexService.getShard(0);
  886. ShardSearchRequest shardSearchRequest = new ShardSearchRequest(
  887. OriginalIndices.NONE, new SearchRequest().allowPartialSearchResults(true),
  888. indexShard.shardId(), 1, new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null);
  889. List<SearchContextId> contextIds = new ArrayList<>();
  890. int numContexts = randomIntBetween(1, 10);
  891. for (int i = 0; i < numContexts; i++) {
  892. SearchService.SearchRewriteContext rewriteContext = searchService.acquireSearcherAndRewrite(shardSearchRequest, indexShard);
  893. final SearchContext searchContext = searchService.createContext(rewriteContext);
  894. assertThat(searchContext.id().getId(), equalTo((long) (i + 1)));
  895. searchService.putContext(searchContext);
  896. contextIds.add(searchContext.id());
  897. }
  898. assertThat(searchService.getActiveContexts(), equalTo(contextIds.size()));
  899. while (contextIds.isEmpty() == false) {
  900. final SearchContextId contextId = randomFrom(contextIds);
  901. assertFalse(searchService.freeContext(new SearchContextId(UUIDs.randomBase64UUID(), contextId.getId())));
  902. assertThat(searchService.getActiveContexts(), equalTo(contextIds.size()));
  903. if (randomBoolean()) {
  904. assertTrue(searchService.freeContext(contextId));
  905. } else {
  906. assertTrue(searchService.freeContext((new SearchContextId("", contextId.getId()))));
  907. }
  908. contextIds.remove(contextId);
  909. assertThat(searchService.getActiveContexts(), equalTo(contextIds.size()));
  910. assertFalse(searchService.freeContext(new SearchContextId("", contextId.getId())));
  911. assertFalse(searchService.freeContext(contextId));
  912. assertThat(searchService.getActiveContexts(), equalTo(contextIds.size()));
  913. }
  914. }
  915. }