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 ed64520ce7e12..9b9af8143561c 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
@@ -862,17 +862,14 @@ public boolean moveShards() {
for (var storedShardMovement : bestNonPreferredShardMovementsTracker.getBestShardMovements()) {
final var shardRouting = storedShardMovement.shardRouting();
final var index = projectIndex(shardRouting);
- // If `shardMoved` is true, there may have been moves that have made our previous move decision
- // invalid, so we must call `decideMove` again. If not, we know we haven't made any moves, and we
- // can use the cached decision.
- final var moveDecision = shardMoved ? decideMove(index, shardRouting) : storedShardMovement.moveDecision();
+ final var moveDecision = refreshDecisionIfRequired(index, storedShardMovement, shardMoved);
if (moveDecision.isDecisionTaken() && moveDecision.cannotRemainAndCanMove()) {
if (notPreferredLogger.isDebugEnabled()) {
notPreferredLogger.debug(
- "Moving shard [{}] to [{}] from a NOT_PREFERRED allocation, explanation is [{}]",
+ "Moving shard [{}] to [{}] from a NOT_PREFERRED allocation: {}",
shardRouting,
moveDecision.getTargetNode().getName(),
- moveDecision.getCanRemainDecision().getExplanation()
+ moveDecision.getCanRemainDecision()
);
}
executeMove(shardRouting, index, moveDecision, "move-non-preferred");
@@ -886,6 +883,46 @@ public boolean moveShards() {
return shardMoved;
}
+ /**
+ * Re-run the allocation deciders if we need to
+ *
+ * Reasons to re-run the deciders include:
+ *
+ * - A shard has been moved since the decision was made, we need to
+ * re-run the deciders to ensure the decision is still valid
+ *
+ * - The {@link #notPreferredLogger} is set to
DEBUG,
+ * we need to re-run the deciders with
+ * {@link org.elasticsearch.cluster.routing.allocation.RoutingAllocation.DebugMode#EXCLUDE_YES_DECISIONS},
+ * so the explanation(s) are populated for the log message
+ *
+ *
+ *
+ * @param index The index of the shard
+ * @param storedShardMovement The existing shard movement decision
+ * @param shardMoved True if a shard moved in this balancing round, false otherwise
+ * @return The move decision to act on, recalculated if necessary
+ */
+ private MoveDecision refreshDecisionIfRequired(
+ ProjectIndex index,
+ BestShardMovementsTracker.StoredShardMovement storedShardMovement,
+ boolean shardMoved
+ ) {
+ if (notPreferredLogger.isDebugEnabled() == false && shardMoved == false) {
+ return storedShardMovement.moveDecision();
+ }
+
+ final var oldDebugMode = allocation.getDebugMode();
+ if (notPreferredLogger.isDebugEnabled()) {
+ allocation.setDebugMode(RoutingAllocation.DebugMode.EXCLUDE_YES_DECISIONS);
+ }
+ try {
+ return decideMove(index, storedShardMovement.shardRouting());
+ } finally {
+ allocation.setDebugMode(oldDebugMode);
+ }
+ }
+
private void executeMove(ShardRouting shardRouting, ProjectIndex index, MoveDecision moveDecision, String reason) {
final ModelNode sourceNode = nodes.get(shardRouting.currentNodeId());
final ModelNode targetNode = nodes.get(moveDecision.getTargetNode().getId());
diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java
index 4afb83255da6f..eff18ab0faeb6 100644
--- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java
+++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java
@@ -1027,7 +1027,7 @@ public Decision canRemain(
RoutingNode node,
RoutingAllocation allocation
) {
- return new Decision.Single(Decision.Type.NOT_PREFERRED, "test_decider", "Always NOT_PREFERRED");
+ return allocation.decision(Decision.NOT_PREFERRED, "test_decider", "Always NOT_PREFERRED");
}
})), clusterState.getRoutingNodes().mutableCopy(), clusterState, ClusterInfo.EMPTY, SnapshotShardSizeInfo.EMPTY, 0L);
@@ -1039,7 +1039,7 @@ public Decision canRemain(
"moved a NOT_PREFERRED allocation",
notPreferredLoggerName,
Level.DEBUG,
- "Moving shard [*] to [*] from a NOT_PREFERRED allocation, explanation is [Always NOT_PREFERRED]"
+ "Moving shard [*] to [*] from a NOT_PREFERRED allocation: [NOT_PREFERRED(Always NOT_PREFERRED)]"
)
);
}