`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
}
            if (results.hasResult(shardIndex)) {
                assert failure == null : "shard failed before but shouldn't: " + failure;
                successfulOps.decrementAndGet(); 
            }
        }
        results.consumeShardFailure(shardIndex);
    }
    private static boolean isTaskCancelledException(Exception e) {
        return ExceptionsHelper.unwrapCausesAndSuppressed(e, ex -> ex instanceof TaskCancelledException).isPresent();
    }
    /**
     * Executed once for every successful shard level request.
     * @param result the result returned form the shard
     * @param shardIt the shard iterator
     */
    protected void onShardResult(Result result, SearchShardIterator shardIt) {
        assert result.getShardIndex() != -1 : "shard index is not set";
        assert result.getSearchShardTarget() != null : "search shard target must not be null";
        hasShardResponse.set(true);
        if (logger.isTraceEnabled()) {
            logger.trace("got first-phase result from {}", result != null ? result.getSearchShardTarget() : null);
        }
        results.consumeResult(result, () -> onShardResultConsumed(result, shardIt));
    }
    private void onShardResultConsumed(Result result, SearchShardIterator shardIt) {
        successfulOps.incrementAndGet();