`org.elasticsearch.action.search.AbstractSearchAsyncAction#clusterState` is almost entirely unused, except for one trace log message and one call to `org.elasticsearch.cluster.ClusterState#getMinTransportVersion`. If the cluster state is changing quickly while searches are starting then these captured cluster states can consume substantial memory. If all we care about is the min transport version then we should just pass that in.

Note that the cluster state that ends up here is also captured by various lambdas (e.g. the one that runs after the can-match phase). The more we can reduce the scope of this cluster state capture, the better.
server/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java
return true;
    }
    private static boolean assertExecuteOnStartThread() {
        
        
        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
         * same thread (because we never went async, or the same thread was selected from the thread pool) or a different thread. If we
         * continue on the same thread in the case that we never went async and this happens repeatedly we will end up recursing deeply and
         * could stack overflow. To prevent this, we fork if we are called back on the same thread that execution started on and otherwise