Browse Source

Save some RoutingNodes Instantiations (#79941)

This commit introduces the ability to create a mutable copy of `RoutingNodes`
instead of having to re-compute the data structures from scratch which is much
faster and allows for some reuse of immutable parts of a routing nodes instance.

Also, this sets up further improvements that avoid some more redundant routing node
instantiations in `RoutingAllocation`.
Armin Braun 3 years ago
parent
commit
a5e3c6d933
18 changed files with 188 additions and 66 deletions
  1. 30 3
      server/src/main/java/org/elasticsearch/cluster/ClusterState.java
  2. 17 0
      server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
  3. 103 29
      server/src/main/java/org/elasticsearch/cluster/routing/RoutingNodes.java
  4. 1 2
      server/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java
  5. 3 2
      server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java
  6. 2 3
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java
  7. 1 1
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/BalancedSingleShardTests.java
  8. 1 1
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java
  9. 1 2
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/SameShardRoutingTests.java
  10. 1 1
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java
  11. 10 3
      server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/DiskThresholdDeciderTests.java
  12. 2 2
      server/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java
  13. 3 7
      server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java
  14. 2 2
      x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ProactiveStorageDeciderServiceTests.java
  15. 8 2
      x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageDeciderDecisionTests.java
  16. 1 1
      x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageDeciderServiceTests.java
  17. 1 1
      x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/allocation/CcrPrimaryFollowerAllocationDeciderTests.java
  18. 1 4
      x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/allocation/SearchableSnapshotAllocatorTests.java

+ 30 - 3
server/src/main/java/org/elasticsearch/cluster/ClusterState.java

@@ -156,8 +156,21 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
         this.customs = customs;
         this.wasReadFromDiff = wasReadFromDiff;
         this.routingNodes = routingNodes;
-        assert routingNodes == null || routingNodes.equals(new RoutingNodes(this))
-            : "RoutingNodes [" + routingNodes + "] are not consistent with this cluster state [" + new RoutingNodes(this) + "]";
+        assert assertConsistentRoutingNodes(routingTable, nodes, routingNodes);
+    }
+
+    private static boolean assertConsistentRoutingNodes(
+        RoutingTable routingTable,
+        DiscoveryNodes nodes,
+        @Nullable RoutingNodes routingNodes
+    ) {
+        if (routingNodes == null) {
+            return true;
+        }
+        final RoutingNodes expected = RoutingNodes.immutable(routingTable, nodes);
+        assert routingNodes.equals(expected)
+            : "RoutingNodes [" + routingNodes + "] are not consistent with this cluster state [" + expected + "]";
+        return true;
     }
 
     public long term() {
@@ -257,10 +270,24 @@ public class ClusterState implements ToXContentFragment, Diffable<ClusterState>
         if (routingNodes != null) {
             return routingNodes;
         }
-        routingNodes = new RoutingNodes(this);
+        routingNodes = RoutingNodes.immutable(routingTable, nodes);
         return routingNodes;
     }
 
+    /**
+     * Returns a fresh mutable copy of the routing nodes view.
+     */
+    public RoutingNodes mutableRoutingNodes() {
+        final RoutingNodes nodes = this.routingNodes;
+        // use the cheaper copy constructor if we already computed the routing nodes for this state.
+        if (nodes != null) {
+            return nodes.mutableCopy();
+        }
+        // we don't have any routing nodes for this state, likely because it's a temporary state in the reroute logic, don't compute an
+        // immutable copy that will never be used and instead directly build a mutable copy
+        return RoutingNodes.mutable(routingTable, this.nodes);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();

+ 17 - 0
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java

@@ -68,6 +68,23 @@ public class RoutingNode implements Iterable<ShardRouting> {
         assert invariant();
     }
 
+    private RoutingNode(RoutingNode original) {
+        this.nodeId = original.nodeId;
+        this.node = original.node;
+        this.shards = new LinkedHashMap<>(original.shards);
+        this.relocatingShards = new LinkedHashSet<>(original.relocatingShards);
+        this.initializingShards = new LinkedHashSet<>(original.initializingShards);
+        this.shardsByIndex = new LinkedHashMap<>(original.shardsByIndex.size());
+        for (Map.Entry<Index, LinkedHashSet<ShardRouting>> entry : original.shardsByIndex.entrySet()) {
+            shardsByIndex.put(entry.getKey(), new LinkedHashSet<>(entry.getValue()));
+        }
+        assert invariant();
+    }
+
+    RoutingNode copy() {
+        return new RoutingNode(this);
+    }
+
     private static LinkedHashMap<ShardId, ShardRouting> buildShardRoutingMap(ShardRouting... shardRoutings) {
         final LinkedHashMap<ShardId, ShardRouting> shards = new LinkedHashMap<>();
         for (ShardRouting shardRouting : shardRoutings) {

+ 103 - 29
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNodes.java

@@ -8,6 +8,8 @@
 
 package org.elasticsearch.cluster.routing;
 
+import com.carrotsearch.hppc.cursors.ObjectCursor;
+
 import org.apache.logging.log4j.Logger;
 import org.apache.lucene.util.CollectionUtil;
 import org.elasticsearch.Assertions;
@@ -15,6 +17,7 @@ import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
 import org.elasticsearch.cluster.routing.UnassignedInfo.AllocationStatus;
 import org.elasticsearch.cluster.routing.allocation.ExistingShardsAllocator;
 import org.elasticsearch.cluster.service.MasterService;
@@ -44,8 +47,9 @@ import java.util.stream.StreamSupport;
 
 /**
  * {@link RoutingNodes} represents a copy the routing information contained in the {@link ClusterState cluster state}.
- * It can be either initialized as mutable or immutable (see {@link #RoutingNodes(ClusterState, boolean)}), allowing
- * or disallowing changes to its elements.
+ * It can be either initialized as mutable or immutable allowing or disallowing changes to its elements.
+ * (see {@link RoutingNodes#mutable(RoutingTable, DiscoveryNodes)}, {@link RoutingNodes#immutable(RoutingTable, DiscoveryNodes)},
+ * and {@link #mutableCopy()})
  *
  * The main methods used to update routing entries are:
  * <ul>
@@ -57,11 +61,11 @@ import java.util.stream.StreamSupport;
  */
 public class RoutingNodes implements Iterable<RoutingNode> {
 
-    private final Map<String, RoutingNode> nodesToShards = new HashMap<>();
+    private final Map<String, RoutingNode> nodesToShards;
 
-    private final UnassignedShards unassignedShards = new UnassignedShards(this);
+    private final UnassignedShards unassignedShards;
 
-    private final Map<ShardId, List<ShardRouting>> assignedShards = new HashMap<>();
+    private final Map<ShardId, List<ShardRouting>> assignedShards;
 
     private final boolean readOnly;
 
@@ -75,21 +79,33 @@ public class RoutingNodes implements Iterable<RoutingNode> {
 
     private int totalShardCount = 0;
 
-    private final Map<String, Set<String>> attributeValuesByAttribute = new HashMap<>();
-    private final Map<String, Recoveries> recoveriesPerNode = new HashMap<>();
+    private final Map<String, Set<String>> attributeValuesByAttribute;
+    private final Map<String, Recoveries> recoveriesPerNode;
+
+    /**
+     * Creates an immutable instance from the {@link RoutingTable} and {@link DiscoveryNodes} found in a cluster state. Used to initialize
+     * the routing nodes in {@link ClusterState#getRoutingNodes()}. This method should not be used directly, use
+     * {@link ClusterState#getRoutingNodes()} instead.
+     */
+    public static RoutingNodes immutable(RoutingTable routingTable, DiscoveryNodes discoveryNodes) {
+        return new RoutingNodes(routingTable, discoveryNodes, true);
+    }
 
-    public RoutingNodes(ClusterState clusterState) {
-        this(clusterState, true);
+    public static RoutingNodes mutable(RoutingTable routingTable, DiscoveryNodes discoveryNodes) {
+        return new RoutingNodes(routingTable, discoveryNodes, false);
     }
 
-    public RoutingNodes(ClusterState clusterState, boolean readOnly) {
+    private RoutingNodes(RoutingTable routingTable, DiscoveryNodes discoveryNodes, boolean readOnly) {
         this.readOnly = readOnly;
-        final RoutingTable routingTable = clusterState.routingTable();
+        this.recoveriesPerNode = new HashMap<>();
+        this.assignedShards = new HashMap<>();
+        this.unassignedShards = new UnassignedShards(this);
+        this.attributeValuesByAttribute = new HashMap<>();
 
-        Map<String, LinkedHashMap<ShardId, ShardRouting>> nodesToShards = new HashMap<>();
+        final Map<String, LinkedHashMap<ShardId, ShardRouting>> nodesToShards = new HashMap<>(discoveryNodes.getDataNodes().size());
         // fill in the nodeToShards with the "live" nodes
-        for (DiscoveryNode node : clusterState.nodes().getDataNodes().values()) {
-            nodesToShards.put(node.getId(), new LinkedHashMap<>()); // LinkedHashMap to preserve order
+        for (ObjectCursor<String> node : discoveryNodes.getDataNodes().keys()) {
+            nodesToShards.put(node.value, new LinkedHashMap<>()); // LinkedHashMap to preserve order
         }
 
         // fill in the inverse of node -> shards allocated
@@ -104,11 +120,9 @@ public class RoutingNodes implements Iterable<RoutingNode> {
                     // by the ShardId, as this is common for primary and replicas.
                     // A replica Set might have one (and not more) replicas with the state of RELOCATING.
                     if (shard.assignedToNode()) {
-                        Map<ShardId, ShardRouting> entries = nodesToShards.computeIfAbsent(
-                            shard.currentNodeId(),
-                            k -> new LinkedHashMap<>()
-                        ); // LinkedHashMap to preserve order
-                        ShardRouting previousValue = entries.put(shard.shardId(), shard);
+                        // LinkedHashMap to preserve order
+                        ShardRouting previousValue = nodesToShards.computeIfAbsent(shard.currentNodeId(), k -> new LinkedHashMap<>())
+                            .put(shard.shardId(), shard);
                         if (previousValue != null) {
                             throw new IllegalArgumentException("Cannot have two different shards with same shard id on same node");
                         }
@@ -118,13 +132,12 @@ public class RoutingNodes implements Iterable<RoutingNode> {
                         }
                         if (shard.relocating()) {
                             relocatingShards++;
-                            // LinkedHashMap to preserve order.
-                            // Add the counterpart shard with relocatingNodeId reflecting the source from which
-                            // it's relocating from.
-                            entries = nodesToShards.computeIfAbsent(shard.relocatingNodeId(), k -> new LinkedHashMap<>());
                             ShardRouting targetShardRouting = shard.getTargetRelocatingShard();
                             addInitialRecovery(targetShardRouting, indexShard.primary);
-                            previousValue = entries.put(targetShardRouting.shardId(), targetShardRouting);
+                            // LinkedHashMap to preserve order.
+                            // Add the counterpart shard with relocatingNodeId reflecting the source from which it's relocating from.
+                            previousValue = nodesToShards.computeIfAbsent(shard.relocatingNodeId(), k -> new LinkedHashMap<>())
+                                .put(targetShardRouting.shardId(), targetShardRouting);
                             if (previousValue != null) {
                                 throw new IllegalArgumentException("Cannot have two different shards with same shard id on same node");
                             }
@@ -142,12 +155,50 @@ public class RoutingNodes implements Iterable<RoutingNode> {
                 }
             }
         }
+        this.nodesToShards = new HashMap<>(nodesToShards.size());
         for (Map.Entry<String, LinkedHashMap<ShardId, ShardRouting>> entry : nodesToShards.entrySet()) {
             String nodeId = entry.getKey();
-            this.nodesToShards.put(nodeId, new RoutingNode(nodeId, clusterState.nodes().get(nodeId), entry.getValue()));
+            this.nodesToShards.put(nodeId, new RoutingNode(nodeId, discoveryNodes.get(nodeId), entry.getValue()));
         }
     }
 
+    private RoutingNodes(RoutingNodes routingNodes) {
+        // we should not call this on mutable instances, it's still expensive to create the copy and callers should instead mutate a single
+        // instance
+        assert routingNodes.readOnly : "tried to create a mutable copy from a mutable instance";
+        this.readOnly = false;
+        this.nodesToShards = new HashMap<>(routingNodes.nodesToShards.size());
+        for (Map.Entry<String, RoutingNode> entry : routingNodes.nodesToShards.entrySet()) {
+            this.nodesToShards.put(entry.getKey(), entry.getValue().copy());
+        }
+        this.assignedShards = new HashMap<>(routingNodes.assignedShards.size());
+        for (Map.Entry<ShardId, List<ShardRouting>> entry : routingNodes.assignedShards.entrySet()) {
+            this.assignedShards.put(entry.getKey(), new ArrayList<>(entry.getValue()));
+        }
+        this.unassignedShards = routingNodes.unassignedShards.copyFor(this);
+
+        this.inactivePrimaryCount = routingNodes.inactivePrimaryCount;
+        this.inactiveShardCount = routingNodes.inactiveShardCount;
+        this.relocatingShards = routingNodes.relocatingShards;
+        this.activeShardCount = routingNodes.activeShardCount;
+        this.totalShardCount = routingNodes.totalShardCount;
+        this.attributeValuesByAttribute = new HashMap<>(routingNodes.attributeValuesByAttribute.size());
+        for (Map.Entry<String, Set<String>> entry : routingNodes.attributeValuesByAttribute.entrySet()) {
+            this.attributeValuesByAttribute.put(entry.getKey(), new HashSet<>(entry.getValue()));
+        }
+        this.recoveriesPerNode = new HashMap<>(routingNodes.recoveriesPerNode.size());
+        for (Map.Entry<String, Recoveries> entry : routingNodes.recoveriesPerNode.entrySet()) {
+            this.recoveriesPerNode.put(entry.getKey(), entry.getValue().copy());
+        }
+    }
+
+    /**
+     * @return a mutable copy of this instance
+     */
+    public RoutingNodes mutableCopy() {
+        return new RoutingNodes(this);
+    }
+
     private void addRecovery(ShardRouting routing) {
         updateRecoveryCounts(routing, true, findAssignedPrimaryIfPeerRecovery(routing));
     }
@@ -864,13 +915,29 @@ public class RoutingNodes implements Iterable<RoutingNode> {
         private final List<ShardRouting> unassigned;
         private final List<ShardRouting> ignored;
 
-        private int primaries = 0;
-        private int ignoredPrimaries = 0;
+        private int primaries;
+        private int ignoredPrimaries;
 
         public UnassignedShards(RoutingNodes nodes) {
+            this(nodes, new ArrayList<>(), new ArrayList<>(), 0, 0);
+        }
+
+        private UnassignedShards(
+            RoutingNodes nodes,
+            List<ShardRouting> unassigned,
+            List<ShardRouting> ignored,
+            int primaries,
+            int ignoredPrimaries
+        ) {
             this.nodes = nodes;
-            unassigned = new ArrayList<>();
-            ignored = new ArrayList<>();
+            this.unassigned = unassigned;
+            this.ignored = ignored;
+            this.primaries = primaries;
+            this.ignoredPrimaries = ignoredPrimaries;
+        }
+
+        public UnassignedShards copyFor(RoutingNodes newNodes) {
+            return new UnassignedShards(newNodes, new ArrayList<>(unassigned), new ArrayList<>(ignored), primaries, ignoredPrimaries);
         }
 
         public void add(ShardRouting shardRouting) {
@@ -1277,6 +1344,13 @@ public class RoutingNodes implements Iterable<RoutingNode> {
         private int incoming = 0;
         private int outgoing = 0;
 
+        public Recoveries copy() {
+            final Recoveries copy = new Recoveries();
+            copy.incoming = incoming;
+            copy.outgoing = outgoing;
+            return copy;
+        }
+
         void addOutgoing(int howMany) {
             assert outgoing + howMany >= 0 : outgoing + howMany + " must be >= 0";
             outgoing += howMany;

+ 1 - 2
server/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java

@@ -44,7 +44,7 @@ import static org.elasticsearch.cluster.metadata.MetadataIndexStateService.isInd
  */
 public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<RoutingTable> {
 
-    public static final RoutingTable EMPTY_ROUTING_TABLE = builder().build();
+    public static final RoutingTable EMPTY_ROUTING_TABLE = new RoutingTable(0, ImmutableOpenMap.of());
 
     private final long version;
 
@@ -407,7 +407,6 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
             }
         }
 
-        @SuppressWarnings("unchecked")
         public Builder updateNodes(long version, RoutingNodes routingNodes) {
             // this is being called without pre initializing the routing table, so we must copy over the version as well
             this.version = version;

+ 3 - 2
server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java

@@ -34,6 +34,7 @@ import org.elasticsearch.cluster.routing.allocation.command.AllocationCommands;
 import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
 import org.elasticsearch.cluster.routing.allocation.decider.Decision;
 import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.collect.ImmutableOpenMap;
 import org.elasticsearch.common.logging.ESLogMessage;
 import org.elasticsearch.gateway.GatewayAllocator;
@@ -316,7 +317,7 @@ public class AllocationService {
             final Metadata.Builder metadataBuilder = Metadata.builder(clusterState.metadata());
             for (Map.Entry<Integer, List<String>> entry : autoExpandReplicaChanges.entrySet()) {
                 final int numberOfReplicas = entry.getKey();
-                final String[] indices = entry.getValue().toArray(new String[entry.getValue().size()]);
+                final String[] indices = entry.getValue().toArray(Strings.EMPTY_ARRAY);
                 // we do *not* update the in sync allocation ids as they will be removed upon the first index
                 // operation which make these copies stale
                 routingTableBuilder.updateNumberOfReplicas(numberOfReplicas, indices);
@@ -614,7 +615,7 @@ public class AllocationService {
      * Create a mutable {@link RoutingNodes}. This is a costly operation so this must only be called once!
      */
     private RoutingNodes getMutableRoutingNodes(ClusterState clusterState) {
-        return new RoutingNodes(clusterState, false);
+        return clusterState.mutableRoutingNodes();
     }
 
     /** override this to control time based decisions during allocation */

+ 2 - 3
server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java

@@ -22,7 +22,6 @@ import org.elasticsearch.cluster.node.DiscoveryNodeRole;
 import org.elasticsearch.cluster.node.DiscoveryNodes;
 import org.elasticsearch.cluster.routing.RecoverySource;
 import org.elasticsearch.cluster.routing.RoutingNode;
-import org.elasticsearch.cluster.routing.RoutingNodes;
 import org.elasticsearch.cluster.routing.RoutingTable;
 import org.elasticsearch.cluster.routing.ShardRouting;
 import org.elasticsearch.cluster.routing.ShardRoutingState;
@@ -734,7 +733,7 @@ public class AllocationCommandsTests extends ESAllocationTestCase {
         MoveAllocationCommand command = new MoveAllocationCommand(index.getName(), 0, "node1", "node2");
         RoutingAllocation routingAllocation = new RoutingAllocation(
             new AllocationDeciders(Collections.emptyList()),
-            new RoutingNodes(clusterState, false),
+            clusterState.mutableRoutingNodes(),
             clusterState,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,
@@ -801,7 +800,7 @@ public class AllocationCommandsTests extends ESAllocationTestCase {
         MoveAllocationCommand command = new MoveAllocationCommand(index.getName(), 0, "node2", "node1");
         RoutingAllocation routingAllocation = new RoutingAllocation(
             new AllocationDeciders(Collections.emptyList()),
-            new RoutingNodes(clusterState, false),
+            clusterState.mutableRoutingNodes(),
             clusterState,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,

+ 1 - 1
server/src/test/java/org/elasticsearch/cluster/routing/allocation/BalancedSingleShardTests.java

@@ -377,7 +377,7 @@ public class BalancedSingleShardTests extends ESAllocationTestCase {
     private RoutingAllocation newRoutingAllocation(AllocationDeciders deciders, ClusterState state) {
         RoutingAllocation allocation = new RoutingAllocation(
             deciders,
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,

+ 1 - 1
server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java

@@ -677,7 +677,7 @@ public class NodeVersionAllocationDeciderTests extends ESAllocationTestCase {
         );
 
         final RoutingChangesObserver routingChangesObserver = new RoutingChangesObserver.AbstractRoutingChangesObserver();
-        final RoutingNodes routingNodes = new RoutingNodes(clusterState, false);
+        final RoutingNodes routingNodes = clusterState.mutableRoutingNodes();
         final ShardRouting startedPrimary = routingNodes.startShard(
             logger,
             routingNodes.initializeShard(primaryShard, "newNode", null, 0, routingChangesObserver),

+ 1 - 2
server/src/test/java/org/elasticsearch/cluster/routing/allocation/SameShardRoutingTests.java

@@ -20,7 +20,6 @@ import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.cluster.node.DiscoveryNode;
 import org.elasticsearch.cluster.node.DiscoveryNodes;
 import org.elasticsearch.cluster.routing.RoutingNode;
-import org.elasticsearch.cluster.routing.RoutingNodes;
 import org.elasticsearch.cluster.routing.RoutingTable;
 import org.elasticsearch.cluster.routing.ShardRouting;
 import org.elasticsearch.cluster.routing.ShardRoutingState;
@@ -200,7 +199,7 @@ public class SameShardRoutingTests extends ESAllocationTestCase {
         RoutingNode routingNode = clusterState.getRoutingNodes().node(primaryShard.currentNodeId());
         RoutingAllocation routingAllocation = new RoutingAllocation(
             new AllocationDeciders(Collections.emptyList()),
-            new RoutingNodes(clusterState, false),
+            clusterState.mutableRoutingNodes(),
             clusterState,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,

+ 1 - 1
server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java

@@ -46,7 +46,7 @@ public class BalancedShardsAllocatorTests extends ESAllocationTestCase {
         ShardRouting shard = clusterState.routingTable().index("idx_new").shard(0).primaryShard();
         RoutingAllocation allocation = new RoutingAllocation(
             new AllocationDeciders(Collections.emptyList()),
-            new RoutingNodes(clusterState, false),
+            RoutingNodes.mutable(clusterState.routingTable(), clusterState.nodes()),
             clusterState,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,

+ 10 - 3
server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/DiskThresholdDeciderTests.java

@@ -929,7 +929,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
         ClusterState clusterState = ClusterState.builder(baseClusterState).routingTable(builder.build()).build();
         RoutingAllocation routingAllocation = new RoutingAllocation(
             null,
-            new RoutingNodes(clusterState),
+            RoutingNodes.immutable(clusterState.routingTable(), clusterState.nodes()),
             clusterState,
             clusterInfo,
             null,
@@ -959,7 +959,14 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
                     .addIndexShard(new IndexShardRoutingTable.Builder(secondRouting.shardId()).addShard(secondRouting).build())
             );
         clusterState = ClusterState.builder(baseClusterState).routingTable(builder.build()).build();
-        routingAllocation = new RoutingAllocation(null, new RoutingNodes(clusterState), clusterState, clusterInfo, null, System.nanoTime());
+        routingAllocation = new RoutingAllocation(
+            null,
+            RoutingNodes.immutable(clusterState.routingTable(), clusterState.nodes()),
+            clusterState,
+            clusterInfo,
+            null,
+            System.nanoTime()
+        );
         routingAllocation.debugDecision(true);
         decision = diskThresholdDecider.canRemain(firstRouting, firstRoutingNode, routingAllocation);
         assertThat(decision.type(), equalTo(Decision.Type.YES));
@@ -1115,7 +1122,7 @@ public class DiskThresholdDeciderTests extends ESAllocationTestCase {
 
         RoutingAllocation routingAllocation = new RoutingAllocation(
             null,
-            new RoutingNodes(clusterState),
+            RoutingNodes.immutable(clusterState.routingTable(), clusterState.nodes()),
             clusterState,
             clusterInfo,
             null,

+ 2 - 2
server/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java

@@ -488,7 +488,7 @@ public class PrimaryShardAllocatorTests extends ESAllocationTestCase {
             .build();
         return new RoutingAllocation(
             allocationDeciders,
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             null,
             new SnapshotShardSizeInfo(ImmutableOpenMap.of()) {
@@ -535,7 +535,7 @@ public class PrimaryShardAllocatorTests extends ESAllocationTestCase {
             .routingTable(routingTableBuilder.build())
             .nodes(DiscoveryNodes.builder().add(node1).add(node2).add(node3))
             .build();
-        return new RoutingAllocation(deciders, new RoutingNodes(state, false), state, null, null, System.nanoTime());
+        return new RoutingAllocation(deciders, state.mutableRoutingNodes(), state, null, null, System.nanoTime());
     }
 
     private void assertClusterHealthStatus(RoutingAllocation allocation, ClusterHealthStatus expectedStatus) {

+ 3 - 7
server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java

@@ -577,7 +577,7 @@ public class ReplicaShardAllocatorTests extends ESAllocationTestCase {
             .build();
         return new RoutingAllocation(
             deciders,
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,
@@ -622,7 +622,7 @@ public class ReplicaShardAllocatorTests extends ESAllocationTestCase {
             .build();
         return new RoutingAllocation(
             deciders,
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,
@@ -650,16 +650,12 @@ public class ReplicaShardAllocatorTests extends ESAllocationTestCase {
     class TestAllocator extends ReplicaShardAllocator {
 
         private Map<DiscoveryNode, TransportNodesListShardStoreMetadata.StoreFilesMetadata> data = null;
-        private AtomicBoolean fetchDataCalled = new AtomicBoolean(false);
+        private final AtomicBoolean fetchDataCalled = new AtomicBoolean(false);
 
         public void clean() {
             data = null;
         }
 
-        public void cleanWithEmptyData() {
-            data = new HashMap<>();
-        }
-
         public boolean getFetchDataCalledAndClean() {
             return fetchDataCalled.getAndSet(false);
         }

+ 2 - 2
x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ProactiveStorageDeciderServiceTests.java

@@ -296,7 +296,7 @@ public class ProactiveStorageDeciderServiceTests extends AutoscalingTestCase {
     private ClusterState randomAllocate(ClusterState state) {
         RoutingAllocation allocation = new RoutingAllocation(
             new AllocationDeciders(List.of()),
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             null,
             null,
@@ -327,7 +327,7 @@ public class ProactiveStorageDeciderServiceTests extends AutoscalingTestCase {
     private ClusterState startAll(ClusterState state) {
         RoutingAllocation allocation = new RoutingAllocation(
             new AllocationDeciders(List.of()),
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             null,
             null,

+ 8 - 2
x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageDeciderDecisionTests.java

@@ -436,8 +436,14 @@ public class ReactiveStorageDeciderDecisionTests extends AutoscalingTestCase {
     }
 
     private static RoutingAllocation createRoutingAllocation(ClusterState state, AllocationDeciders allocationDeciders) {
-        RoutingNodes routingNodes = new RoutingNodes(state, false);
-        return new RoutingAllocation(allocationDeciders, routingNodes, state, createClusterInfo(state), null, System.nanoTime());
+        return new RoutingAllocation(
+            allocationDeciders,
+            state.mutableRoutingNodes(),
+            state,
+            createClusterInfo(state),
+            null,
+            System.nanoTime()
+        );
     }
 
     private void withRoutingAllocation(Consumer<RoutingAllocation> block) {

+ 1 - 1
x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageDeciderServiceTests.java

@@ -233,7 +233,7 @@ public class ReactiveStorageDeciderServiceTests extends AutoscalingTestCase {
             .shardRoutingTable(indexMetadata.getIndex().getName(), shardId);
         RoutingAllocation allocation = new RoutingAllocation(
             new AllocationDeciders(List.of()),
-            new RoutingNodes(initialClusterState, false),
+            initialClusterState.mutableRoutingNodes(),
             initialClusterState,
             null,
             null,

+ 1 - 1
x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/allocation/CcrPrimaryFollowerAllocationDeciderTests.java

@@ -183,7 +183,7 @@ public class CcrPrimaryFollowerAllocationDeciderTests extends ESAllocationTestCa
         final AllocationDecider decider = new CcrPrimaryFollowerAllocationDecider();
         final RoutingAllocation routingAllocation = new RoutingAllocation(
             new AllocationDeciders(List.of(decider)),
-            new RoutingNodes(clusterState),
+            RoutingNodes.immutable(clusterState.routingTable(), clusterState.nodes()),
             clusterState,
             ClusterInfo.EMPTY,
             SnapshotShardSizeInfo.EMPTY,

+ 1 - 4
x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/allocation/SearchableSnapshotAllocatorTests.java

@@ -61,7 +61,6 @@ public class SearchableSnapshotAllocatorTests extends ESAllocationTestCase {
     public void testAllocateToNodeWithLargestCache() {
         final ShardId shardId = new ShardId("test", "_na_", 0);
         final List<DiscoveryNode> nodes = randomList(1, 10, () -> newNode("node-" + UUIDs.randomBase64UUID(random())));
-        final DiscoveryNode localNode = randomFrom(nodes);
         final DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue();
 
         final Metadata metadata = buildSingleShardIndexMetadata(shardId);
@@ -136,7 +135,6 @@ public class SearchableSnapshotAllocatorTests extends ESAllocationTestCase {
     public void testNoFetchesOnDeciderNo() {
         final ShardId shardId = new ShardId("test", "_na_", 0);
         final List<DiscoveryNode> nodes = randomList(1, 10, () -> newNode("node-" + UUIDs.randomBase64UUID(random())));
-        final DiscoveryNode localNode = randomFrom(nodes);
         final DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue();
 
         final Metadata metadata = buildSingleShardIndexMetadata(shardId);
@@ -176,7 +174,6 @@ public class SearchableSnapshotAllocatorTests extends ESAllocationTestCase {
     public void testNoFetchesForPartialIndex() {
         final ShardId shardId = new ShardId("test", "_na_", 0);
         final List<DiscoveryNode> nodes = randomList(1, 10, () -> newNode("node-" + UUIDs.randomBase64UUID(random())));
-        final DiscoveryNode localNode = randomFrom(nodes);
         final DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue();
 
         final Metadata metadata = buildSingleShardIndexMetadata(shardId, builder -> builder.put(SNAPSHOT_PARTIAL_SETTING.getKey(), true));
@@ -255,7 +252,7 @@ public class SearchableSnapshotAllocatorTests extends ESAllocationTestCase {
     ) {
         return new RoutingAllocation(
             allocationDeciders,
-            new RoutingNodes(state, false),
+            state.mutableRoutingNodes(),
             state,
             null,
             new SnapshotShardSizeInfo(ImmutableOpenMap.of()) {