diff --git a/server/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java b/server/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java index e3a539a58b837..8a2752fde140b 100644 --- a/server/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java @@ -251,6 +251,32 @@ private boolean checkMinimumVersion(GroupShardsIterator sha return true; } + private boolean assertExecuteOnStartThread() { + // Ensure that the current code has the following stacktrace: + // AbstractSearchAsyncAction#start -> AbstractSearchAsyncAction#executePhase -> AbstractSearchAsyncAction#performPhaseOnShard + final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + assert stackTraceElements.length >= 6 : stackTraceElements; + int index = 0; + assert stackTraceElements[index++].getMethodName().equals("getStackTrace"); + assert stackTraceElements[index++].getMethodName().equals("assertExecuteOnStartThread"); + assert stackTraceElements[index++].getMethodName().equals("performPhaseOnShard"); + if (stackTraceElements[index].getMethodName().equals("performPhaseOnShard")) { + assert stackTraceElements[index].getClassName().endsWith("CanMatchPreFilterSearchPhase"); + index++; + } + assert stackTraceElements[index].getClassName().endsWith("AbstractSearchAsyncAction"); + assert stackTraceElements[index++].getMethodName().equals("run"); + + assert stackTraceElements[index].getClassName().endsWith("AbstractSearchAsyncAction"); + assert stackTraceElements[index++].getMethodName().equals("executePhase"); + + assert stackTraceElements[index].getClassName().endsWith("AbstractSearchAsyncAction"); + assert stackTraceElements[index++].getMethodName().equals("start"); + + assert stackTraceElements[index].getClassName().endsWith("AbstractSearchAsyncAction") == false; + return true; + } + protected void performPhaseOnShard(final int shardIndex, final SearchShardIterator shardIt, final SearchShardTarget shard) { /* * We capture the thread that this phase is starting on. When we are called back after executing the phase, we are either on the @@ -260,9 +286,10 @@ protected void performPhaseOnShard(final int shardIndex, final SearchShardIterat * we can continue (cf. InitialSearchPhase#maybeFork). */ if (shard == null) { + assert assertExecuteOnStartThread(); SearchShardTarget unassignedShard = new SearchShardTarget(null, shardIt.shardId(), shardIt.getClusterAlias(), shardIt.getOriginalIndices()); - fork(() -> onShardFailure(shardIndex, unassignedShard, shardIt, new NoShardAvailableActionException(shardIt.shardId()))); + onShardFailure(shardIndex, unassignedShard, shardIt, new NoShardAvailableActionException(shardIt.shardId())); } else { final PendingExecutions pendingExecutions = throttleConcurrentRequests ? pendingExecutionsPerNode.computeIfAbsent(shard.getNodeId(), n -> new PendingExecutions(maxConcurrentRequestsPerNode))