### CI Link

https://gradle-enterprise.elastic.co/s/ptwgurvvtzx5s

### Repro line

Probably with `./gradlew :qa:full-cluster-restart:v7.0.1#upgradedClusterTest`

### Does it reproduce?

Didn't try

### Applicable branches

main

### Failure history

_No response_

### Failure excerpt

See https://gradle-enterprise.elastic.co/s/ptwgurvvtzx5s/console-log/raw?task=:qa:full-cluster-restart:v7.0.1%23upgradedClusterTest

It also fails for other BWC versions: https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+main+periodic+bwc/
(7.0.0 to 7.1.1)

```
» [2022-11-09T13:11:28,587][ERROR][o.e.b.ElasticsearchUncaughtExceptionHandler] [v7.0.1-1] fatal error in thread [elasticsearch[v7.0.1-1][masterService#updateTask][T#1]], exiting java.lang.AssertionError: {[testclosedindices][0]=2}
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceReconciler.allocateUnassignedInvariant(DesiredBalanceReconciler.java:118)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceReconciler.run(DesiredBalanceReconciler.java:81)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator.recordTime(DesiredBalanceShardsAllocator.java:299)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator.reconcile(DesiredBalanceShardsAllocator.java:213)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator$ReconcileDesiredBalanceExecutor.lambda$applyBalance$1(DesiredBalanceShardsAllocator.java:277)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.AllocationService.reroute(AllocationService.java:518)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.AllocationService.executeWithRoutingAllocation(AllocationService.java:444)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.ClusterModule.reconcile(ClusterModule.java:145)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator$ReconcileDesiredBalanceExecutor.applyBalance(DesiredBalanceShardsAllocator.java:275)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator$ReconcileDesiredBalanceExecutor.execute(DesiredBalanceShardsAllocator.java:261)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.MasterService.innerExecuteTasks(MasterService.java:1052)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.MasterService.executeTasks(MasterService.java:1017)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.MasterService.runTasks(MasterService.java:278)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.MasterService$Batcher.run(MasterService.java:170)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.TaskBatcher.runIfNotProcessed(TaskBatcher.java:110)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.cluster.service.TaskBatcher$BatchedTask.run(TaskBatcher.java:148)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:850)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.runAndClean(PrioritizedEsThreadPoolExecutor.java:257)
»  	at org.elasticsearch.server@8.6.0-SNAPSHOT/org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.run(PrioritizedEsThreadPoolExecutor.java:223)
»  	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
»  	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
»  	at java.base/java.lang.Thread.run(Thread.java:833)
```
server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceReconciler.java
return secondary;
        };
        /*
         * we use 2 arrays and move replicas to the second array once we allocated an identical
         * replica in the current iteration to make sure all indices get allocated in the same manner.
         * The arrays are sorted by primaries first and then by index and shard ID so a 2 indices with
         * 2 replica and 1 shard would look like:
         * [(0,P,IDX1), (0,P,IDX2), (0,R,IDX1), (0,R,IDX1), (0,R,IDX2), (0,R,IDX2)]
         * if we allocate for instance (0, R, IDX1) we move the second replica to the secondary array and proceed with
         * the next replica. If we could not find a node to allocate (0,R,IDX1) we move all it's replicas to ignoreUnassigned.
         */
        ShardRouting[] primary = unassigned.drain();
        ShardRouting[] secondary = new ShardRouting[primary.length];
        int secondaryLength = 0;
        int primaryLength = primary.length;
        ArrayUtil.timSort(primary, comparator);
        do {
            nextShard: for (int i = 0; i < primaryLength; i++) {
                final var shard = primary[i];
                final var assignment = desiredBalance.getAssignment(shard.shardId());
                final var isThrottled = new AtomicBoolean(false);
                if (assignment != null) {
                    for (final var nodeIdIterator : List.of(
                        getDesiredNodesIds(shard, assignment),
                        getFallbackNodeIds(shard, isThrottled)
                    )) {
                        for (final var desiredNodeId : nodeIdIterator) {
                            final var routingNode = routingNodes.node(desiredNodeId);
                            if (routingNode == null) {