Skip to content

Conversation

@DaveCTurner
Copy link
Contributor

Today the WriteLoadConstraintDecider constructs various strings
explaining its decisions, but it only uses these messages if debug
logging is enabled or it's responding to an allocation explain request.
This commit skips the unnecessary work on the hot path.

Today the `WriteLoadConstraintDecider` constructs various strings
explaining its decisions, but it only uses these messages if debug
logging is enabled or it's responding to an allocation explain request.
This commit skips the unnecessary work on the hot path.
@DaveCTurner DaveCTurner added >non-issue :Distributed Coordination/Allocation All issues relating to the decision making around placing a shard (both master logic & on the nodes) labels Nov 7, 2025
@elasticsearchmachine elasticsearchmachine added Team:Distributed Coordination Meta label for Distributed Coordination team v9.3.0 labels Nov 7, 2025
@elasticsearchmachine
Copy link
Collaborator

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

@DiannaHohensee
Copy link
Contributor

Maybe you could document

/**
* Create a routing decision, including the reason if the debug flag is
* turned on
* @param decision decision whether to allow/deny allocation
* @param deciderLabel a human readable label for the AllocationDecider
* @param reason a format string explanation of the decision
* @param params format string parameters
*/
public Decision decision(Decision decision, String deciderLabel, String reason, Object... params) {
if (debugDecision()) {
return Decision.single(decision.type(), deciderLabel, reason, params);
} else {
return decision;
}
}
a bit more to suggest callers keep the input inexpensive if calling frequently, or call a debug check themselves? A warning that the cost often adds up quickly.

Perhaps a bit obvious, but I did it, so here we are :)

@DiannaHohensee
Copy link
Contributor

Though Decision.single was used instead of the RoutingAllocation helper. That would suggest documentation / a warning in the Decision class 🤔

Copy link
Contributor

@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.

LGTM, thanks for fixing it!

Left various suggestions with the aim of avoiding a repeat situation.

if (logger.isDebugEnabled()) {
logInterventionMessage.maybeExecute(() -> logger.debug(explain));
}
return allocation.decision(Decision.NOT_PREFERRED, NAME, explain);
Copy link
Contributor

Choose a reason for hiding this comment

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

allocation.decision will technically check the debug again, redundantly.

Maybe something more dramatic like allocation.decisionWithOptionalExplain would be more clear in the decision between RoutingAllocation#decision() and Decision#single() 😌

Anyway, I don't feel strongly about any of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not redundant, we could be here because debug logging is enabled but still we don't want to construct a new Decision when we can just return Decision.NOT_PREFERRED.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, you're right 👍

}

String explanation = Strings.format(
return allocation.decision(
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this also have a if-else around allocation.debugDecision()?

Either for performance of not bothering with some of the string creation, or to model a consistent choice in the code for future readers.

I wonder if having RoutingAllocation#decision confuses things too much in the first place. What amount of string creation is OK vs not OK?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No need here, none of the parameters allocate anything new, they're just trivial accessor methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't passing "Shard [%s] in index [%s] can be assigned to node [%s]. The node's utilization would become [%s]" as a parameter create a String?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, string literals are constants, they're only allocated once. The bytecode that calls RoutingAllocation#decision just uses a simple ldc to push the string constant onto the stack, no allocation needed:

$ javap -p -c server/build/classes/java/main/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.class | grep -A25 'utilization would become'
     526: ldc           #214                // String Shard [%s] in index [%s] can be assigned to node [%s]. The node\'s utilization would become [%s]
     528: iconst_4
     529: anewarray     #77                 // class java/lang/Object
     532: dup
     533: iconst_0
     534: aload_1
     535: invokevirtual #102                // Method org/elasticsearch/cluster/routing/ShardRouting.shardId:()Lorg/elasticsearch/index/shard/ShardId;
     538: aastore
     539: dup
     540: iconst_1
     541: aload_1
     542: invokevirtual #216                // Method org/elasticsearch/cluster/routing/ShardRouting.index:()Lorg/elasticsearch/index/Index;
     545: aastore
     546: dup
     547: iconst_2
     548: aload_2
     549: invokevirtual #122                // Method org/elasticsearch/cluster/routing/RoutingNode.nodeId:()Ljava/lang/String;
     552: aastore
     553: dup
     554: iconst_3
     555: fload         11
     557: invokestatic  #172                // Method java/lang/Float.valueOf:(F)Ljava/lang/Float;
     560: aastore
     561: invokevirtual #79                 // Method org/elasticsearch/cluster/routing/allocation/RoutingAllocation.decision:(Lorg/elasticsearch/cluster/routing/allocation/decider/Decision;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Lorg/elasticsearch/cluster/routing/allocation/decider/Decision;
     564: areturn

@DaveCTurner
Copy link
Contributor Author

Added some docs in 50d1fb8.

@DaveCTurner DaveCTurner enabled auto-merge (squash) November 7, 2025 18:01
@DaveCTurner DaveCTurner merged commit 2525ef3 into elastic:main Nov 7, 2025
34 checks passed
@DaveCTurner DaveCTurner deleted the 2025/11/07/WriteLoadConstraintDecider-explain-unnecessary branch November 7, 2025 21:17
Kubik42 pushed a commit to Kubik42/elasticsearch that referenced this pull request Nov 10, 2025
…ic#137755)

Today the `WriteLoadConstraintDecider` constructs various strings
explaining its decisions, but it only uses these messages if debug
logging is enabled or it's responding to an allocation explain request.
This commit skips the unnecessary work on the hot path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Distributed Coordination/Allocation All issues relating to the decision making around placing a shard (both master logic & on the nodes) >non-issue Team:Distributed Coordination Meta label for Distributed Coordination team v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants