From 53bfa9b46a115344b9a7b08adf9c94c2a1c29c97 Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 15:23:39 +1000 Subject: [PATCH 1/8] Implement AllocationDeciders#findNonPreferred --- .../allocator/BalancedShardsAllocator.java | 81 +++++++++++++++++++ .../allocation/decider/AllocationDecider.java | 11 +++ .../decider/AllocationDeciders.java | 13 +++ 3 files changed, 105 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java index c737b89b80f73..fce9451502b83 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java @@ -155,6 +155,7 @@ public void allocate(RoutingAllocation allocation) { final Balancer balancer = new Balancer(writeLoadForecaster, allocation, balancerSettings.getThreshold(), balancingWeights); balancer.allocateUnassigned(); balancer.moveShards(); + balancer.moveNonPreferred(); balancer.balance(); // Node weights are calculated after each internal balancing round and saved to the RoutingNodes copy. @@ -711,6 +712,77 @@ protected int comparePivot(int j) { return indices; } + /** + * Move started shards that are in non-preferred allocations + */ + public void moveNonPreferred() { + for (Iterator it = allocation.deciders().findNonPreferred(allocation); it.hasNext();) { + ShardRouting shardRouting = it.next(); + ProjectIndex index = projectIndex(shardRouting); + final MoveDecision moveDecision = decideMoveNonPreferred(index, shardRouting); + if (moveDecision.isDecisionTaken() && moveDecision.forceMove()) { + final ModelNode sourceNode = nodes.get(shardRouting.currentNodeId()); + final ModelNode targetNode = nodes.get(moveDecision.getTargetNode().getId()); + sourceNode.removeShard(index, shardRouting); + Tuple relocatingShards = routingNodes.relocateShard( + shardRouting, + targetNode.getNodeId(), + allocation.clusterInfo().getShardSize(shardRouting, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE), + "non-preferred", + allocation.changes() + ); + final ShardRouting shard = relocatingShards.v2(); + targetNode.addShard(projectIndex(shard), shard); + if (logger.isTraceEnabled()) { + logger.trace("Moved shard [{}] to node [{}]", shardRouting, targetNode.getRoutingNode()); + } + } else if (moveDecision.isDecisionTaken() && moveDecision.canRemain() == false) { + logger.trace("[{}][{}] can't move", shardRouting.index(), shardRouting.id()); + } + } + } + + /** + * Makes a decision on whether to move a started shard to another node. The following rules apply + * to the {@link MoveDecision} return object: + * 1. If the shard is not started, no decision will be taken and {@link MoveDecision#isDecisionTaken()} will return false. + * 2. If the shard's current allocation is preferred ({@link Decision.Type#YES}), no attempt will be made to move the shard and + * {@link MoveDecision#getCanRemainDecision} will have a decision type of YES. All other fields in the object will be null. + * 3. If the shard is not allowed ({@link Decision.Type#NO}), or not preferred ({@link Decision.Type#NOT_PREFERRED}) to remain + * on its current node, then {@link MoveDecision#getAllocationDecision()} will be populated with the decision of moving to + * another node. If {@link MoveDecision#forceMove()} returns {@code true}, then {@link MoveDecision#getTargetNode} will return + * a non-null value representing a node that returned {@link Decision.Type#YES} from canAllocate, otherwise the assignedNodeId + * will be null. + * 4. If the method is invoked in explain mode (e.g. from the cluster allocation explain APIs), then + * {@link MoveDecision#getNodeDecisions} will have a non-null value. + */ + public MoveDecision decideMoveNonPreferred(final ProjectIndex index, final ShardRouting shardRouting) { + NodeSorter sorter = nodeSorters.sorterForShard(shardRouting); + index.assertMatch(shardRouting); + + if (shardRouting.started() == false) { + // we can only move started shards + return MoveDecision.NOT_TAKEN; + } + + final ModelNode sourceNode = nodes.get(shardRouting.currentNodeId()); + assert sourceNode != null && sourceNode.containsShard(index, shardRouting); + RoutingNode routingNode = sourceNode.getRoutingNode(); + Decision canRemain = allocation.deciders().canRemain(shardRouting, routingNode, allocation); + if (canRemain.type() != Type.NOT_PREFERRED || canRemain.type() != Type.NO) { + return MoveDecision.remain(canRemain); + } + + sorter.reset(index); + /* + * the sorter holds the minimum weight node first for the shards index. + * We now walk through the nodes until we find a node to allocate the shard. + * This is not guaranteed to be balanced after this operation we still try best effort to + * allocate on the minimal eligible node. + */ + return decideMove(sorter, shardRouting, sourceNode, canRemain, this::decideCanAllocatePreferredOnly); + } + /** * Move started shards that can not be allocated to a node anymore * @@ -839,6 +911,15 @@ private MoveDecision decideMove( ); } + private Decision decideCanAllocatePreferredOnly(ShardRouting shardRouting, RoutingNode target) { + Decision decision = allocation.deciders().canAllocate(shardRouting, target, allocation); + // not-preferred means no here + if (decision.type() == Type.NOT_PREFERRED) { + return Decision.NO; + } + return decision; + } + private Decision decideCanAllocate(ShardRouting shardRouting, RoutingNode target) { // don't use canRebalance as we want hard filtering rules to apply. See #17698 return allocation.deciders().canAllocate(shardRouting, target, allocation); diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java index 7fae18a332f0c..3eb585812ce98 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type; +import java.util.Iterator; import java.util.Optional; import java.util.Set; @@ -153,4 +154,14 @@ public Decision canAllocateReplicaWhenThereIsRetentionLease(ShardRouting shardRo public Optional> getForcedInitialShardAllocationToNodes(ShardRouting shardRouting, RoutingAllocation allocation) { return Optional.empty(); } + + /** + * Return a list of shard allocations that are non-preferable according to this decider + * + * @param allocation the current routing allocation + * @return A list of shard allocations that this decider would like to move elsewhere, in order of descending priority + */ + public Optional> getNonPreferredAllocations(RoutingAllocation allocation) { + return Optional.empty(); + } } diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java index 6a09c894dbc7d..cbf924425be8c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java @@ -17,9 +17,12 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.util.set.Sets; +import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; @@ -244,4 +247,14 @@ public Optional> getForcedInitialShardAllocationToNodes(ShardRouting } return result; } + + @SuppressWarnings("unchecked") + public Iterator findNonPreferred(RoutingAllocation routingAllocation) { + var iterators = new ArrayList>(deciders.length); + for (AllocationDecider decider : deciders) { + decider.getNonPreferredAllocations(routingAllocation).ifPresent(iterators::add); + assert iterators.size() == 1 : "when we've got more than one decider contributing we should revisit how these are combined"; + } + return Iterators.concat(iterators.>toArray(Iterator[]::new)); + } } From f0f9f77e87a7ffe2a99d225a32b35c6f4993d8aa Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 16:13:37 +1000 Subject: [PATCH 2/8] Fix assertion --- .../cluster/routing/allocation/decider/AllocationDeciders.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java index cbf924425be8c..f9d49ae53a11f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java @@ -253,7 +253,7 @@ public Iterator findNonPreferred(RoutingAllocation routingAllocati var iterators = new ArrayList>(deciders.length); for (AllocationDecider decider : deciders) { decider.getNonPreferredAllocations(routingAllocation).ifPresent(iterators::add); - assert iterators.size() == 1 : "when we've got more than one decider contributing we should revisit how these are combined"; + assert iterators.size() <= 1 : "when we've got more than one decider contributing we should revisit how these are combined"; } return Iterators.concat(iterators.>toArray(Iterator[]::new)); } From 71232d5e38111f25b29687a402d7d729f1b3c00c Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 17:41:02 +1000 Subject: [PATCH 3/8] Implement prioritisable problems --- .../allocator/BalancedShardsAllocator.java | 24 +++++++++++++++--- .../allocation/decider/AllocationDecider.java | 8 +++--- .../decider/AllocationDeciders.java | 25 +++++++++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java index fce9451502b83..d2631bc95abeb 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java @@ -716,8 +716,24 @@ protected int comparePivot(int j) { * Move started shards that are in non-preferred allocations */ public void moveNonPreferred() { - for (Iterator it = allocation.deciders().findNonPreferred(allocation); it.hasNext();) { - ShardRouting shardRouting = it.next(); + boolean movedAShard = false; + do { + for (Iterator problemIterator = allocation.deciders() + .findAllocationProblems(allocation); problemIterator.hasNext();) { + AllocationDeciders.AllocationProblem problem = problemIterator.next(); + if (tryResolveAllocationProblem(problem)) { + movedAShard = true; + break; + } + logger.debug("Unable to resolve allocation problem [{}], will try next time", problem); + } + // TODO: Update cluster info + } while (movedAShard); + } + + private boolean tryResolveAllocationProblem(AllocationDeciders.AllocationProblem problem) { + for (Iterator shardIterator = problem.preferredShardMovements(); shardIterator.hasNext();) { + ShardRouting shardRouting = shardIterator.next(); ProjectIndex index = projectIndex(shardRouting); final MoveDecision moveDecision = decideMoveNonPreferred(index, shardRouting); if (moveDecision.isDecisionTaken() && moveDecision.forceMove()) { @@ -728,7 +744,7 @@ public void moveNonPreferred() { shardRouting, targetNode.getNodeId(), allocation.clusterInfo().getShardSize(shardRouting, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE), - "non-preferred", + problem.relocateReason(), allocation.changes() ); final ShardRouting shard = relocatingShards.v2(); @@ -736,10 +752,12 @@ public void moveNonPreferred() { if (logger.isTraceEnabled()) { logger.trace("Moved shard [{}] to node [{}]", shardRouting, targetNode.getRoutingNode()); } + return true; } else if (moveDecision.isDecisionTaken() && moveDecision.canRemain() == false) { logger.trace("[{}][{}] can't move", shardRouting.index(), shardRouting.id()); } } + return false; } /** diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java index 3eb585812ce98..45cbe413245c2 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java @@ -16,7 +16,7 @@ import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type; -import java.util.Iterator; +import java.util.Collection; import java.util.Optional; import java.util.Set; @@ -156,12 +156,12 @@ public Optional> getForcedInitialShardAllocationToNodes(ShardRouting } /** - * Return a list of shard allocations that are non-preferable according to this decider + * Get a list of allocation problems that can be fixed by moving some shards * * @param allocation the current routing allocation - * @return A list of shard allocations that this decider would like to move elsewhere, in order of descending priority + * @return A list of node IDs that contain shards this decider would like to move elsewhere, in order of descending priority */ - public Optional> getNonPreferredAllocations(RoutingAllocation allocation) { + public Optional> getAllocationProblems(RoutingAllocation allocation) { return Optional.empty(); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java index f9d49ae53a11f..db5d6aa9e1523 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java @@ -17,14 +17,14 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.util.set.Sets; -import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.Iterator; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import java.util.function.BiFunction; import java.util.function.Function; @@ -248,13 +248,22 @@ public Optional> getForcedInitialShardAllocationToNodes(ShardRouting return result; } - @SuppressWarnings("unchecked") - public Iterator findNonPreferred(RoutingAllocation routingAllocation) { - var iterators = new ArrayList>(deciders.length); + public Iterator findAllocationProblems(RoutingAllocation routingAllocation) { + var problems = new TreeSet<>(Comparator.comparing(AllocationProblem::priority).reversed()); for (AllocationDecider decider : deciders) { - decider.getNonPreferredAllocations(routingAllocation).ifPresent(iterators::add); - assert iterators.size() <= 1 : "when we've got more than one decider contributing we should revisit how these are combined"; + decider.getAllocationProblems(routingAllocation).ifPresent(problems::addAll); + } + return problems.iterator(); + } + + public interface AllocationProblem { + + Iterator preferredShardMovements(); + + String relocateReason(); + + default int priority() { + return 1; } - return Iterators.concat(iterators.>toArray(Iterator[]::new)); } } From c31b05af11ed707c8512fae09600790df39908bf Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 17:49:25 +1000 Subject: [PATCH 4/8] Javadoc --- .../allocation/decider/AllocationDeciders.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java index db5d6aa9e1523..650379a521e89 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.routing.RoutingChangesObserver; import org.elasticsearch.cluster.routing.RoutingNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; @@ -258,10 +259,21 @@ public Iterator findAllocationProblems(RoutingAllocation rout public interface AllocationProblem { + /** + * Shard movements to attempt to resolve the problem in descending priority order. + */ Iterator preferredShardMovements(); + /** + * The reason for the relocation + * + * @see RoutingChangesObserver#relocationStarted(ShardRouting, ShardRouting, String) + */ String relocateReason(); + /** + * We could prioritize them this way + */ default int priority() { return 1; } From 967e76e150bb9493351632375cf1ef8e11177736 Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 17:59:01 +1000 Subject: [PATCH 5/8] Example for write load constraint decider --- .../allocation/decider/AllocationDecider.java | 2 +- .../decider/WriteLoadConstraintDecider.java | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java index 45cbe413245c2..35ddd7874c265 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecider.java @@ -161,7 +161,7 @@ public Optional> getForcedInitialShardAllocationToNodes(ShardRouting * @param allocation the current routing allocation * @return A list of node IDs that contain shards this decider would like to move elsewhere, in order of descending priority */ - public Optional> getAllocationProblems(RoutingAllocation allocation) { + public Optional> getAllocationProblems(RoutingAllocation allocation) { return Optional.empty(); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.java index e814f570a67bb..1cc254df16145 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.java @@ -22,6 +22,11 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.threadpool.ThreadPool; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; +import java.util.stream.Collectors; + /** * Decides whether shards can be allocated to cluster nodes, or can remain on cluster nodes, based on the target node's current write thread * pool usage stats and any candidate shard's write load estimate. @@ -109,6 +114,47 @@ public Decision canRemain(IndexMetadata indexMetadata, ShardRouting shardRouting return Decision.single(Decision.Type.YES, NAME, "canRemain() is not yet implemented"); } + @Override + public Optional> getAllocationProblems(RoutingAllocation allocation) { + if (writeLoadConstraintSettings.getWriteLoadConstraintEnabled().notFullyEnabled()) { + return Optional.empty(); + } + + final var nodeUsageStatsForThreadPools = allocation.clusterInfo().getNodeUsageStatsForThreadPools(); + final Collection hotSpots = nodeUsageStatsForThreadPools.entrySet() + .stream() + .filter(entry -> entry.getValue().threadPoolUsageStatsMap().containsKey(ThreadPool.Names.WRITE)) + .filter(entry -> { + long maxQueueLatency = entry.getValue() + .threadPoolUsageStatsMap() + .get(ThreadPool.Names.WRITE) + .maxThreadPoolQueueLatencyMillis(); + return maxQueueLatency > writeLoadConstraintSettings.getQueueLatencyThreshold().millis(); + }) + .map(entry -> new HotSpot(entry.getKey())) + .collect(Collectors.toList()); + return hotSpots.isEmpty() == false ? Optional.of(hotSpots) : Optional.empty(); + } + + private record HotSpot(String nodeId) implements AllocationDeciders.AllocationProblem { + + @Override + public Iterator preferredShardMovements() { + // TODO: return shards in priority order + return null; + } + + @Override + public String relocateReason() { + return "hot-spotting"; + } + + @Override + public String toString() { + return "Hot-spotting on node " + nodeId; + } + } + /** * Calculates the change to the node's write thread pool utilization percentage if the shard is added to the node. * Returns the percent thread pool utilization change. From d147a18129ac2d6641bdd5d450d6273d7c78d317 Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 18:00:06 +1000 Subject: [PATCH 6/8] Fix text --- .../routing/allocation/allocator/BalancedShardsAllocator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java index d2631bc95abeb..fcc1d4a9997e0 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java @@ -725,7 +725,7 @@ public void moveNonPreferred() { movedAShard = true; break; } - logger.debug("Unable to resolve allocation problem [{}], will try next time", problem); + logger.debug("Unable to resolve [{}]", problem); } // TODO: Update cluster info } while (movedAShard); From 4f7b51974914afb9487ca7c4a6e72e7548aacada Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Mon, 8 Sep 2025 18:00:54 +1000 Subject: [PATCH 7/8] Tidy --- .../routing/allocation/allocator/BalancedShardsAllocator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java index fcc1d4a9997e0..fb26e4de23c91 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java @@ -721,7 +721,7 @@ public void moveNonPreferred() { for (Iterator problemIterator = allocation.deciders() .findAllocationProblems(allocation); problemIterator.hasNext();) { AllocationDeciders.AllocationProblem problem = problemIterator.next(); - if (tryResolveAllocationProblem(problem)) { + if (tryResolve(problem)) { movedAShard = true; break; } @@ -731,7 +731,7 @@ public void moveNonPreferred() { } while (movedAShard); } - private boolean tryResolveAllocationProblem(AllocationDeciders.AllocationProblem problem) { + private boolean tryResolve(AllocationDeciders.AllocationProblem problem) { for (Iterator shardIterator = problem.preferredShardMovements(); shardIterator.hasNext();) { ShardRouting shardRouting = shardIterator.next(); ProjectIndex index = projectIndex(shardRouting); From 91ee1970f20d2bfdf6a6d732ac1b9651ae4890ce Mon Sep 17 00:00:00 2001 From: Nick Tindall Date: Tue, 9 Sep 2025 12:01:40 +1000 Subject: [PATCH 8/8] Fix boolean logic --- .../routing/allocation/allocator/BalancedShardsAllocator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java index fb26e4de23c91..e2cad43308284 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java @@ -787,7 +787,7 @@ public MoveDecision decideMoveNonPreferred(final ProjectIndex index, final Shard assert sourceNode != null && sourceNode.containsShard(index, shardRouting); RoutingNode routingNode = sourceNode.getRoutingNode(); Decision canRemain = allocation.deciders().canRemain(shardRouting, routingNode, allocation); - if (canRemain.type() != Type.NOT_PREFERRED || canRemain.type() != Type.NO) { + if ((canRemain.type() == Type.NOT_PREFERRED || canRemain.type() == Type.NO) == false) { return MoveDecision.remain(canRemain); }