diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java index 2724f903a0b..8ea7e1e34b5 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchService.java +++ b/server/src/main/java/org/elasticsearch/search/SearchService.java @@ -694,14 +694,14 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv } private DefaultSearchContext createSearchContext(SearchRewriteContext rewriteContext, TimeValue timeout) { - final ShardSearchRequest request = rewriteContext.request; - final Engine.Searcher searcher = rewriteContext.searcher; - IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); - IndexShard indexShard = indexService.getShard(request.shardId().getId()); - SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().getId(), - indexShard.shardId(), request.getClusterAlias(), OriginalIndices.NONE); boolean success = false; try { + final ShardSearchRequest request = rewriteContext.request; + final Engine.Searcher searcher = rewriteContext.searcher; + IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); + IndexShard indexShard = indexService.getShard(request.shardId().getId()); + SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().getId(), + indexShard.shardId(), request.getClusterAlias(), OriginalIndices.NONE); DefaultSearchContext searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, searcher, clusterService, indexService, indexShard, bigArrays, threadPool::relativeTimeInMillis, timeout, fetchPhase, clusterService.state().nodes().getMinNodeVersion()); @@ -709,8 +709,9 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv return searchContext; } finally { if (success == false) { - // we handle the case where the DefaultSearchContext constructor throws an exception since we would otherwise - // leak a searcher and this can have severe implications (unable to obtain shard lock exceptions). + // we handle the case where `IndicesService#indexServiceSafe`or `IndexService#getShard`, or the DefaultSearchContext + // constructor throws an exception since we would otherwise leak a searcher and this can have severe implications + // (unable to obtain shard lock exceptions). IOUtils.closeWhileHandlingException(rewriteContext.searcher); } } diff --git a/server/src/test/java/org/elasticsearch/recovery/RelocationIT.java b/server/src/test/java/org/elasticsearch/recovery/RelocationIT.java index 95544134c95..377541c1cdd 100644 --- a/server/src/test/java/org/elasticsearch/recovery/RelocationIT.java +++ b/server/src/test/java/org/elasticsearch/recovery/RelocationIT.java @@ -87,6 +87,7 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -457,7 +458,7 @@ public class RelocationIT extends ESIntegTestCase { } } - public void testIndexAndRelocateConcurrently() throws Exception { + public void testIndexSearchAndRelocateConcurrently() throws Exception { int halfNodes = randomIntBetween(1, 3); Settings[] nodeSettings = Stream.concat( Stream.generate(() -> Settings.builder().put("node.attr.color", "blue").build()).limit(halfNodes), @@ -474,8 +475,21 @@ public class RelocationIT extends ESIntegTestCase { .put("index.routing.allocation.exclude.color", "blue") .put(indexSettings()) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(halfNodes - 1)); + if (randomBoolean()) { + settings.put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), randomIntBetween(1, 10) + "s"); + } assertAcked(prepareCreate("test", settings)); assertAllShardsOnNodes("test", redNodes); + AtomicBoolean stopped = new AtomicBoolean(false); + Thread[] searchThreads = randomBoolean() ? new Thread[0] : new Thread[randomIntBetween(1, 4)]; + for (int i = 0; i < searchThreads.length; i++) { + searchThreads[i] = new Thread(() -> { + while (stopped.get() == false) { + assertNoFailures(client().prepareSearch("test").setRequestCache(false).get()); + } + }); + searchThreads[i].start(); + } int numDocs = randomIntBetween(100, 150); ArrayList ids = new ArrayList<>(); logger.info(" --> indexing [{}] docs", numDocs); @@ -513,7 +527,10 @@ public class RelocationIT extends ESIntegTestCase { assertNoFailures(afterRelocation); assertSearchHits(afterRelocation, ids.toArray(new String[ids.size()])); } - + stopped.set(true); + for (Thread searchThread : searchThreads) { + searchThread.join(); + } } public void testRelocateWhileWaitingForRefresh() { diff --git a/server/src/test/java/org/elasticsearch/search/SearchServiceTests.java b/server/src/test/java/org/elasticsearch/search/SearchServiceTests.java index ab6b1af4a23..9d19082e2c3 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchServiceTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchServiceTests.java @@ -898,4 +898,35 @@ public class SearchServiceTests extends ESSingleNodeTestCase { latch.await(); } } + + public void testDeleteIndexWhileSearch() throws Exception { + createIndex("test"); + int numDocs = randomIntBetween(1, 20); + for (int i = 0; i < numDocs; i++) { + client().prepareIndex("test", "_doc").setSource("f", "v").get(); + } + client().admin().indices().prepareRefresh("test").get(); + AtomicBoolean stopped = new AtomicBoolean(false); + Thread[] searchers = new Thread[randomIntBetween(1, 4)]; + CountDownLatch latch = new CountDownLatch(searchers.length); + for (int i = 0; i < searchers.length; i++) { + searchers[i] = new Thread(() -> { + latch.countDown(); + while (stopped.get() == false) { + try { + client().prepareSearch("test").setRequestCache(false).get(); + } catch (Exception ignored) { + return; + } + } + }); + searchers[i].start(); + } + latch.await(); + client().admin().indices().prepareDelete("test").get(); + stopped.set(true); + for (Thread searcher : searchers) { + searcher.join(); + } + } }