diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java index bd72f475a7566..0d7110a77597e 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java @@ -121,10 +121,14 @@ public static ClusterAllocationExplanation explainShard( public static ShardRouting findShardToExplain(ClusterAllocationExplainRequest request, RoutingAllocation allocation) { ShardRouting foundShard = null; if (request.useAnyUnassignedShard()) { - // If we can use any shard, just pick the first unassigned one (if there are any) - RoutingNodes.UnassignedShards.UnassignedIterator ui = allocation.routingNodes().unassigned().iterator(); - if (ui.hasNext()) { - foundShard = ui.next(); + // If we can use any shard, return the first unassigned primary (if there is one) or the first unassigned replica (if not) + for (ShardRouting unassigned : allocation.routingNodes().unassigned()) { + if (foundShard == null || unassigned.primary()) { + foundShard = unassigned; + } + if (foundShard.primary()) { + break; + } } if (foundShard == null) { throw new IllegalArgumentException("No shard was specified in the request which means the response should explain a " + diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainActionTests.java index cc2386b50b1f6..7fb692f273256 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainActionTests.java @@ -10,7 +10,10 @@ import org.elasticsearch.action.support.replication.ClusterStateCreationUtils; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.routing.IndexRoutingTable; +import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.RoutingNode; +import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.UnassignedInfo; @@ -114,6 +117,33 @@ public void testFindAnyUnassignedShardToExplain() { shard = findShardToExplain(request, routingAllocation(clusterState)); assertEquals(clusterState.getRoutingTable().index("idx").shard(0).replicaShards().get(0), shard); + // prefer unassigned primary to replica + clusterState = ClusterStateCreationUtils.stateWithAssignedPrimariesAndReplicas(new String[]{"idx1", "idx2"}, 1, 1); + final String redIndex = randomBoolean() ? "idx1" : "idx2"; + final RoutingTable.Builder routingTableBuilder = RoutingTable.builder(clusterState.routingTable()); + for (final IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { + final IndexRoutingTable.Builder indexBuilder = new IndexRoutingTable.Builder(indexRoutingTable.getIndex()); + for (final IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { + final IndexShardRoutingTable.Builder shardBuilder = new IndexShardRoutingTable.Builder(indexShardRoutingTable.shardId()); + for (final ShardRouting shardRouting : indexShardRoutingTable) { + if (shardRouting.primary() == false || indexRoutingTable.getIndex().getName().equals(redIndex)) { + // move all replicas and one primary to unassigned + shardBuilder.addShard(shardRouting.moveToUnassigned(new UnassignedInfo( + UnassignedInfo.Reason.ALLOCATION_FAILED, + "test"))); + } else { + shardBuilder.addShard(shardRouting); + } + } + indexBuilder.addIndexShard(shardBuilder.build()); + } + routingTableBuilder.add(indexBuilder); + } + clusterState = ClusterState.builder(clusterState).routingTable(routingTableBuilder.build()).build(); + request = new ClusterAllocationExplainRequest(); + shard = findShardToExplain(request, routingAllocation(clusterState)); + assertEquals(clusterState.getRoutingTable().index(redIndex).shard(0).primaryShard(), shard); + // no unassigned shard to explain final ClusterState allStartedClusterState = ClusterStateCreationUtils.state("idx", randomBoolean(), ShardRoutingState.STARTED, ShardRoutingState.STARTED);