Skip to content

Comments

Return NOT_PREFERRED decisions in allocation explain#137228

Merged
elasticsearchmachine merged 36 commits intoelastic:mainfrom
DiannaHohensee:2025/10/23/ES-12833
Dec 16, 2025
Merged

Return NOT_PREFERRED decisions in allocation explain#137228
elasticsearchmachine merged 36 commits intoelastic:mainfrom
DiannaHohensee:2025/10/23/ES-12833

Conversation

@DiannaHohensee
Copy link
Contributor

@DiannaHohensee DiannaHohensee commented Oct 28, 2025

Adds numerous NOT_PREFERRED options to allocation decision / status types.

Adds NOT_PREFERRED option to AllocationDecision (resolving ES-12729).

A significant change is to re-order comparison of Decision.Type enum
values, such that THROTTLE is chosen over NOT_PREFERRED. Functionally
this change should not matter because simulation (DesiredBalance
computation) does not throttle and reconciliation (real shard movement)
treats not-preferred essentially as a YES: they are not compared.

Closes ES-12833, ES-13288, ES-12729

@DiannaHohensee DiannaHohensee self-assigned this Oct 28, 2025
@DiannaHohensee DiannaHohensee force-pushed the 2025/10/23/ES-12833 branch 4 times, most recently from b03f11f to be58da7 Compare October 30, 2025 20:21
@DiannaHohensee DiannaHohensee force-pushed the 2025/10/23/ES-12833 branch 6 times, most recently from 13322ec to 17de33c Compare November 8, 2025 02:07
Adds numerous NOT_PREFERRED options
to allocation decision / status types.

Adds NOT_PREFERRED option to
AllocationDecision (resolving ES-12729).

Closes ES-12833, ES-13288, ES-12729
@DiannaHohensee DiannaHohensee changed the title [DRAFT] Return NOT_PREFERRED decisions in allocation explain Return NOT_PREFERRED decisions in allocation explain Nov 8, 2025
@DiannaHohensee DiannaHohensee marked this pull request as ready for review November 8, 2025 02:28
@elasticsearchmachine elasticsearchmachine added the needs:triage Requires assignment of a team area label label Nov 8, 2025
@DiannaHohensee DiannaHohensee added >non-issue :Distributed/Allocation All issues relating to the decision making around placing a shard (both master logic & on the nodes) :Distributed Coordination/Distributed Team:Distributed Coordination (obsolete) Meta label for Distributed Coordination team. Obsolete. Please do not use. and removed needs:triage Requires assignment of a team area label :Distributed Coordination/Distributed labels Nov 8, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-distributed-coordination (Team:Distributed Coordination)

@DiannaHohensee
Copy link
Contributor Author

DiannaHohensee commented Nov 8, 2025

Adding @DaveCTurner as a reviewer in case he wants to take a high level look for anything I'm missing -- I saw in git-blame that he wrote the Explanations.java file a while ago. But optional.

@DiannaHohensee
Copy link
Contributor Author

DiannaHohensee commented Nov 8, 2025

I think I might be missing test coverage (if not functionality) of the allocate unassigned code path. However, I've been hacking on the rebalancing code path for a while now to get this all to work sensibly, so if agreeable I might just file another ticket to explore that.

}
if (allocationDecision.type().higherThan(bestDecision)) {
assert allocationDecision.type() != Type.THROTTLE
: "DesiredBalance computations run in a simulation mode and should not encounter throttling";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, how do we know we're simulating here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this as extra protection because I changed the Decision.Type enum ordering, such that the higherThan gate above would now prefer (return true for) THROTTLE over NOT_PREFERRED.

Because this is simulation, we should never see THROTTLE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this. Tripped an explain test, because explain uses the decideMove path, too.

I moved the !Throttle check higher up next to the canAllocate call, and added || !(IsSimulating)

assert decider.canRebalance(shardRouting, allocation).type() != Decision.Type.THROTTLE
: decider.getClass().getSimpleName() + " throttled unexpectedly in canRebalance";
return decider.canRebalance(shardRouting, allocation);
}, (decider, decision) -> Strings.format("Can not rebalance [%s]. [%s]: %s", shardRouting, decider, decision));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConcurrentRebalanceAllocationDecider appears to return THROTTLE from canRebalance ?

Nit: I'd prefer if we stored the result of canRebalance then ran the assertion after it rather than running canRebalance twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, reverted 👍

NOT_PREFERRED,
// Temporarily throttled is a better choice than choosing a not-preferred node,
// but NOT_PREFERRED and THROTTLED are generally not comparable.
THROTTLE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can do this can we? It'll mean in the presence of a THROTTLE and a NOT_PREFERRED, canAllocate will return NOT_PREFERRED, which I think is bad for non-desired-balance allocation, because there we should wait for a THROTTLE to become a yes before allocating to a NOT_PREFERRED?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe there's currently any direct comparison between THROTTLE and NOT_PREFERRED. But we still need to specify an ordering here, to not break how one or the other gets compared to YES and NO.

It'll mean in the presence of a THROTTLE and a NOT_PREFERRED, canAllocate will return NOT_PREFERRED

I think this would be the other way around, THROTTLE would be higher value, closer to YES, and chosen over NOT_PREFERRED. So, IIUC, we both want it the current way.

I've a little blurb for the commit message:

"A significant change is to re-order comparison of Decision.Type enum
values, such that THROTTLE is chosen over NOT_PREFERRED. Functionally
this change should not matter because simulation (DesiredBalance
computation) does not throttle and reconciliation (real shard movement)
treats not-preferred essentially as a YES: they are not compared."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah I think it all makes sense now.

Though in reconciler in allocateUnassigned we still don't handle NOT_PREFERRED but there's a ticket to fix that, and I don't think it's important.

}
// TODO (ES-13482): clusterRebalanceDecision is set to the result of AllocationDecider#canRebalance, which does not return
// NOT_PREFERRED or THROTTLE. This switch statement, and how MoveDecision uses clusterRebalanceDecision, should be
// refactored.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it still does? see org/elasticsearch/cluster/routing/allocation/decider/ConcurrentRebalanceAllocationDecider.java:182

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Simon committed some changes concurrently with my PR.

But I think I was wrong, anyway: I didn't realize the complexities of canRebalance.

Removed 👍 Thanks!

Copy link
Contributor Author

@DiannaHohensee DiannaHohensee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a logic change / bug fix, per my comment. Your assertion suggestion got me rethinking.

} else if (bestDecision == Type.NOT_PREFERRED) {
assert remainDecision.type() != Type.NOT_PREFERRED;
assert remainDecision.type() != Type.NOT_PREFERRED || allocation.isSimulating() == false;
// If we don't ever find a YES decision, we'll settle for NOT_PREFERRED as preferable to NO.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If we don't ever find a YES decision, we'll settle for NOT_PREFERRED as preferable to NO.
// If we don't ever find a YES/THROTTLE decision, we'll settle for NOT_PREFERRED as preferable to NO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied 👍

}
} else if (bestDecision == Type.NOT_PREFERRED) {
assert remainDecision.type() != Type.NOT_PREFERRED;
assert remainDecision.type() != Type.NOT_PREFERRED || allocation.isSimulating() == false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is a bit confusing

assert remainDecision.type() != Type.NOT_PREFERRED || allocation.isSimulating() == false;

Do we need that isSimulating() == false clause? It looks like we should never get here when

allocationDecision.type() == Type.NOT_PREFERRED && remainDecision.type() == Type.NOT_PREFERRED

Or have I missed something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that scenario is short circuited on L1049.
I forgot and got !isSimulating-happy after adding throttle below for the explain path.

Copy link
Contributor

@nicktindall nicktindall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM, a couple of questions

@DiannaHohensee DiannaHohensee added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Dec 16, 2025
@elasticsearchmachine elasticsearchmachine merged commit 95b0836 into elastic:main Dec 16, 2025
35 checks passed
@DiannaHohensee DiannaHohensee deleted the 2025/10/23/ES-12833 branch December 16, 2025 20:08
ywangd added a commit to ywangd/elasticsearch that referenced this pull request Jan 12, 2026
ywangd added a commit to ywangd/elasticsearch that referenced this pull request Jan 13, 2026
eranweiss-elastic pushed a commit to eranweiss-elastic/elasticsearch that referenced this pull request Jan 15, 2026
spinscale pushed a commit to spinscale/elasticsearch that referenced this pull request Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) :Distributed/Allocation All issues relating to the decision making around placing a shard (both master logic & on the nodes) >non-issue Team:Distributed Coordination (obsolete) Meta label for Distributed Coordination team. Obsolete. Please do not use. v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants