### Description

`SearchResponse.Clusters` currently uses a `HashMap<String, AtomicReference>` for storing Cluster objects. The AtomicReference CAS swaps are done in a do/while loop in most cases (e.g., in `CCSSingleCoordinatorSearchProgressListener`).

This can be replaced with a `ConcurrentHashMap` in conjunction with either `compute()` method or the `replace(key, assumed_previous_value, value)` method for atomically swapping in a new Cluster object with a CAS operation, assuring that the updates don't lose data from competing threads doing separate Cluster object updates.

See: https://github.com/elastic/elasticsearch/pull/98457/files#r1310467586 
server/src/main/java/org/elasticsearch/action/search/CCSSingleCoordinatorSearchProgressListener.java
* For example if the first call to onPartialReduce has 5 shards, the second call will
     * have those same 5 shards plus the new batch. onFinalReduce will see all those
     * shards one final time.
     *
     * @param shards The list of shards that are part of this reduce.
     * @param totalHits The total number of hits in this reduce.
     * @param aggs The partial result for aggregations.
     * @param reducePhase The version number for this reduce.
     */
    @Override
    public void onPartialReduce(List<SearchShard> shards, TotalHits totalHits, InternalAggregations aggs, int reducePhase) {
        Map<String, Integer> totalByClusterAlias = partitionCountsByClusterAlias(shards);
        for (Map.Entry<String, Integer> entry : totalByClusterAlias.entrySet()) {
            String clusterAlias = entry.getKey();
            int successfulCount = entry.getValue().intValue();
            AtomicReference<SearchResponse.Cluster> clusterRef = clusters.getCluster(clusterAlias);
            boolean swapped;
            do {
                SearchResponse.Cluster curr = clusterRef.get();
                SearchResponse.Cluster.Status status = curr.getStatus();
                if (status != SearchResponse.Cluster.Status.RUNNING) {
                    
                    break;
                }
                TimeValue took = null;
                int successfulShards = successfulCount + curr.getSkippedShards();
                if (successfulShards == curr.getTotalShards()) {
                    status = curr.isTimedOut() ? SearchResponse.Cluster.Status.PARTIAL : SearchResponse.Cluster.Status.SUCCESSFUL;
                    took = new TimeValue(timeProvider.buildTookInMillis());
                } else if (successfulShards + curr.getFailedShards() == curr.getTotalShards()) {