-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Faster access to INITIALIZING/RELOCATING shards #47817
Conversation
…e cost time of updating ClusterState
Pinging @elastic/es-distributed (:Distributed/Allocation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @kkewwei, I left a few minor requests inline. I also think we should add a method bool invariant()
and call assert invariant()
at the end of the constructor and at the start and end of the add()
, remove()
and update()
methods. The invariant()
method should assert that various invariants hold, e.g. that initializingShards
contains all the initialising shards in shards
in the correct order, and similarly for relocatingShards
. See org.elasticsearch.index.seqno.ReplicationTracker
for a good example of this pattern.
We should also have some unit tests that exercise these methods to verify that the invariant holds.
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
Your suggestion is very helpful, I will modify it according to your suggestion. I have a little doubt, if there is need to call assert invariant() at the end of the constructor, because initializingShards and relocatingShards are created here. |
Yes, it's good practice to verify invariants at the end of the constructor. It may be obvious today that all assertions pass on the newly-constructed object, but the point of the exercise is to catch mistakes that may be made in future too. |
Hi @kkewwei please don't force-push to PR branches, it loses history and makes it harder to keep track of older reviews. I see you've made a couple of changes. Let us know when it's ready for another review. |
Ok, I will not change previous submissions anymore. It's ready to review. should I open another PR fot this, or continue to review here? |
Here is good, thanks. You can push more changes onto a PR branch, just don't force-push since that loses older changes. I might not get to this for the next few days, but it's on my list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @kkewwei, I left a handful more comments.
Another contributor opened a similar PR, but I'd prefer to keep going with this one. They did however make a related change to ThrottlingAllocationDecider
that I would like to do here too:
This doesn't need a specific test, since it's already covered adequately by ThrottlingAllocationTests
.
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
Outdated
Show resolved
Hide resolved
|
||
// shards must contains all the shards in initializingShards and relocatingShards | ||
assert initializingShards.stream().allMatch(shardRouting -> shards.containsValue(shardRouting)); | ||
assert relocatingShards.stream().allMatch(shardRouting -> shards.containsValue(shardRouting)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a stronger property, namely that the order of the shards in relocatingShards
is consistent with that in shards
:
assert relocatingShards.stream().allMatch(shardRouting -> shards.containsValue(shardRouting)); | |
assert new ArrayList<>(relocatingShards) | |
.equals(shards.values().stream().filter(ShardRouting::relocating).collect(Collectors.toList())); |
(this means the implementation needs some adjustment to get the tests to pass again)
|
||
private boolean invariant() { | ||
// initializingShards only contains the initializing shards | ||
assert initializingShards.stream().allMatch(shardRouting -> shardRouting.initializing()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be unnecessary with the suggested changes to check equality below.
assert initializingShards.stream().allMatch(shardRouting -> shardRouting.initializing()); | ||
|
||
// relocatingShards only contains the relocating shards | ||
assert relocatingShards.stream().allMatch(shardRouting -> shardRouting.relocating()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be unnecessary with the suggested changes to check equality below.
assertThat(routingNode.shardsWithState(ShardRoutingState.INITIALIZING).size(), equalTo(1)); | ||
} | ||
|
||
public void testshardsWithState() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void testshardsWithState() { | |
public void testShardsWithStateInIndex() { |
TBR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The invariant is still not checking that the cached collections have matching iteration orders.
@DaveCTurner , I realize what you means, looking forward to your review |
@elasticmachine test this please |
The failure (at least, the one of |
I set the type of in |
I see, yes, this is trickier than I thought. I will have another look at how important the iteration order is. In the meantime, @elasticmachine test this please |
@elasticmachine update branch |
@elasticmachine test this please |
https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+pull-request+multijob+packaging-tests-windows-gcp-sample/253/console, @DaveCTurner , is there anything I could do or fix? |
Failure looks unrelated to this, let's try again before digging deeper. @elasticmachine please run elasticsearch-ci/packaging-sample-matrix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it does look like the order of these filtered lists isn't so important so let's go with the weaker property. I did a (hopefully final) pass and left a few suggestions, otherwise I think this is good to go.
|
||
if (shard.initializing()) { | ||
initializingShards.add(shard); | ||
} else if(shard.relocating()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace nit:
} else if(shard.relocating()) { | |
} else if (shard.relocating()) { |
@@ -127,6 +178,14 @@ void remove(ShardRouting shard) { | |||
* @return number of shards | |||
*/ | |||
public int numberOfShardsWithState(ShardRoutingState... states) { | |||
if (states.length == 1) { | |||
if(states[0] == ShardRoutingState.INITIALIZING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace nit:
if(states[0] == ShardRoutingState.INITIALIZING) { | |
if (states[0] == ShardRoutingState.INITIALIZING) { |
@@ -144,6 +203,14 @@ public int numberOfShardsWithState(ShardRoutingState... states) { | |||
* @return List of shards | |||
*/ | |||
public List<ShardRouting> shardsWithState(ShardRoutingState... states) { | |||
if (states.length == 1) { | |||
if(states[0] == ShardRoutingState.INITIALIZING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace nit:
if(states[0] == ShardRoutingState.INITIALIZING) { | |
if (states[0] == ShardRoutingState.INITIALIZING) { |
@@ -164,6 +231,26 @@ public int numberOfShardsWithState(ShardRoutingState... states) { | |||
public List<ShardRouting> shardsWithState(String index, ShardRoutingState... states) { | |||
List<ShardRouting> shards = new ArrayList<>(); | |||
|
|||
if (states.length == 1) { | |||
if(states[0] == ShardRoutingState.INITIALIZING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace nit:
if(states[0] == ShardRoutingState.INITIALIZING) { | |
if (states[0] == ShardRoutingState.INITIALIZING) { |
} | ||
|
||
void remove(ShardRouting shard) { | ||
ShardRouting previousValue = shards.remove(shard.shardId()); | ||
assert previousValue == shard : "expected shard " + previousValue + " but was " + shard; | ||
if (shard.initializing()) { | ||
boolean exist = initializingShards.remove(shard); | ||
assert exist : "expected shard " + shard + "exists in initializingShards, but not "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wording/whitespace suggestion:
assert exist : "expected shard " + shard + "exists in initializingShards, but not "; | |
assert exist : "expected shard " + shard + " to exist in initializingShards"; |
assert exist : "expected shard " + shard + "exists in initializingShards, but not "; | ||
} else if (shard.relocating()) { | ||
boolean exist = relocatingShards.remove(shard); | ||
assert exist : "expected shard " + shard + "exists in relocatingShards, but not "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wording/whitespace suggestion:
assert exist : "expected shard " + shard + "exists in relocatingShards, but not "; | |
assert exist : "expected shard " + shard + " to exist in relocatingShards"; |
@DaveCTurner, very thank you for your grammar check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM thanks @kkewwei
@elasticmachine test this please |
Today a couple of allocation deciders iterate through all the shards on a node to find the `INITIALIZING` or `RELOCATING` ones, and this can slow down cluster state updates in clusters with very high-density nodes holding many thousands of shards even if those shards belong to closed or frozen indices. This commit pre-computes the sets of `INITIALIZING` and `RELOCATING` shards to speed up this search. Closes #46941 Relates #48579 Co-authored-by: "hongju.xhj" <hongju.xhj@alibaba-inc.com>
Today a couple of allocation deciders iterate through all the shards on a node
to find the
INITIALIZING
orRELOCATING
ones, and this can slow down clusterstate updates in clusters with very high-density nodes holding many thousands
of shards even if those shards belong to closed or frozen indices. This commit
pre-computes the sets of
INITIALIZING
andRELOCATING
shards to speed upthis search.
Closes #46941
Relates #48579
Co-authored-by: "hongju.xhj" hongju.xhj@alibaba-inc.com