|
46 | 46 | import org.elasticsearch.common.xcontent.XContentBuilder; |
47 | 47 | import org.elasticsearch.index.Index; |
48 | 48 | import org.elasticsearch.index.IndexModule; |
| 49 | +import org.elasticsearch.index.IndexNotFoundException; |
49 | 50 | import org.elasticsearch.index.IndexService; |
50 | 51 | import org.elasticsearch.index.IndexSettings; |
51 | 52 | import org.elasticsearch.index.query.AbstractQueryBuilder; |
@@ -122,7 +123,7 @@ protected boolean resetNodeAfterTest() { |
122 | 123 | @Override |
123 | 124 | protected Collection<Class<? extends Plugin>> getPlugins() { |
124 | 125 | return pluginList(FailOnRewriteQueryPlugin.class, CustomScriptPlugin.class, |
125 | | - ReaderWrapperCountPlugin.class, InternalOrPrivateSettingsPlugin.class); |
| 126 | + ReaderWrapperCountPlugin.class, InternalOrPrivateSettingsPlugin.class, MockSearchService.TestPlugin.class); |
126 | 127 | } |
127 | 128 |
|
128 | 129 | public static class ReaderWrapperCountPlugin extends Plugin { |
@@ -329,6 +330,7 @@ public void onFailure(Exception e) { |
329 | 330 | service.executeFetchPhase(req, new SearchShardTask(123L, "", "", "", null, Collections.emptyMap()), listener); |
330 | 331 | listener.get(); |
331 | 332 | if (useScroll) { |
| 333 | + // have to free context since this test does not remove the index from IndicesService. |
332 | 334 | service.freeContext(searchPhaseResult.getContextId()); |
333 | 335 | } |
334 | 336 | } catch (ExecutionException ex) { |
@@ -357,6 +359,53 @@ public void onFailure(Exception e) { |
357 | 359 | assertEquals(0, totalStats.getFetchCurrent()); |
358 | 360 | } |
359 | 361 |
|
| 362 | + public void testSearchWhileIndexDeletedDoesNotLeakSearchContext() throws ExecutionException, InterruptedException { |
| 363 | + createIndex("index"); |
| 364 | + client().prepareIndex("index", "type", "1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get(); |
| 365 | + |
| 366 | + IndicesService indicesService = getInstanceFromNode(IndicesService.class); |
| 367 | + IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index")); |
| 368 | + IndexShard indexShard = indexService.getShard(0); |
| 369 | + |
| 370 | + MockSearchService service = (MockSearchService) getInstanceFromNode(SearchService.class); |
| 371 | + service.setOnPutContext( |
| 372 | + context -> { |
| 373 | + if (context.indexShard() == indexShard) { |
| 374 | + assertAcked(client().admin().indices().prepareDelete("index")); |
| 375 | + } |
| 376 | + } |
| 377 | + ); |
| 378 | + |
| 379 | + SearchRequest searchRequest = new SearchRequest().allowPartialSearchResults(true); |
| 380 | + SearchRequest scrollSearchRequest = new SearchRequest().allowPartialSearchResults(true) |
| 381 | + .scroll(new Scroll(TimeValue.timeValueMinutes(1))); |
| 382 | + |
| 383 | + // the scrolls are not explicitly freed, but should all be gone when the test finished. |
| 384 | + // for completeness, we also randomly test the regular search path. |
| 385 | + final boolean useScroll = randomBoolean(); |
| 386 | + PlainActionFuture<SearchPhaseResult> result = new PlainActionFuture<>(); |
| 387 | + service.executeQueryPhase( |
| 388 | + new ShardSearchRequest(OriginalIndices.NONE, useScroll ? scrollSearchRequest : searchRequest, |
| 389 | + new ShardId(resolveIndex("index"), 0), 1, |
| 390 | + new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f, -1, null, null), |
| 391 | + new SearchShardTask(123L, "", "", "", null, Collections.emptyMap()), result); |
| 392 | + |
| 393 | + try { |
| 394 | + result.get(); |
| 395 | + } catch (Exception e) { |
| 396 | + // ok |
| 397 | + } |
| 398 | + |
| 399 | + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareGetIndex().setIndices("index").get()); |
| 400 | + |
| 401 | + assertEquals(0, service.getActiveContexts()); |
| 402 | + |
| 403 | + SearchStats.Stats totalStats = indexShard.searchStats().getTotal(); |
| 404 | + assertEquals(0, totalStats.getQueryCurrent()); |
| 405 | + assertEquals(0, totalStats.getScrollCurrent()); |
| 406 | + assertEquals(0, totalStats.getFetchCurrent()); |
| 407 | + } |
| 408 | + |
360 | 409 | public void testTimeout() throws IOException { |
361 | 410 | createIndex("index"); |
362 | 411 | final SearchService service = getInstanceFromNode(SearchService.class); |
@@ -527,6 +576,8 @@ public void testMaxOpenScrollContexts() throws RuntimeException { |
527 | 576 | SearchService.MAX_OPEN_SCROLL_CONTEXT.get(Settings.EMPTY) + "]. " + |
528 | 577 | "This limit can be set by changing the [search.max_open_scroll_context] setting.", |
529 | 578 | ex.getMessage()); |
| 579 | + |
| 580 | + service.freeAllScrollContexts(); |
530 | 581 | } |
531 | 582 |
|
532 | 583 | public void testOpenScrollContextsConcurrently() throws Exception { |
|
0 commit comments