Skip to content

Commit 464f769

Browse files
authored
Use comparator for Reconfigurator (elastic#42283)
Simplifies the voting configuration reconfiguration logic by switching to an explicit Comparator for the priorities. Does not make changes to the behavior of the component.
1 parent fccb7a2 commit 464f769

File tree

1 file changed

+66
-66
lines changed

1 file changed

+66
-66
lines changed

server/src/main/java/org/elasticsearch/cluster/coordination/Reconfigurator.java

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,10 @@
2727
import org.elasticsearch.common.settings.Setting;
2828
import org.elasticsearch.common.settings.Setting.Property;
2929
import org.elasticsearch.common.settings.Settings;
30-
import org.elasticsearch.common.util.set.Sets;
3130

32-
import java.util.Collection;
33-
import java.util.Collections;
3431
import java.util.Set;
35-
import java.util.SortedSet;
3632
import java.util.TreeSet;
3733
import java.util.stream.Collectors;
38-
import java.util.stream.Stream;
3934

4035
/**
4136
* Computes the optimal configuration of voting nodes in the cluster.
@@ -102,81 +97,86 @@ public VotingConfiguration reconfigure(Set<DiscoveryNode> liveNodes, Set<String>
10297
logger.trace("{} reconfiguring {} based on liveNodes={}, retiredNodeIds={}, currentMaster={}",
10398
this, currentConfig, liveNodes, retiredNodeIds, currentMaster);
10499

105-
/*
106-
* There are three true/false properties of each node in play: live/non-live, retired/non-retired and in-config/not-in-config.
107-
* Firstly we divide the nodes into disjoint sets based on these properties:
108-
*
109-
* - nonRetiredMaster
110-
* - nonRetiredNotMasterInConfigNotLiveIds
111-
* - nonRetiredInConfigLiveIds
112-
* - nonRetiredLiveNotInConfigIds
113-
*
114-
* The other 5 possibilities are not relevant:
115-
* - retired, in-config, live -- retired nodes should be removed from the config
116-
* - retired, in-config, non-live -- retired nodes should be removed from the config
117-
* - retired, not-in-config, live -- cannot add a retired node back to the config
118-
* - retired, not-in-config, non-live -- cannot add a retired node back to the config
119-
* - non-retired, non-live, not-in-config -- no evidence this node exists at all
120-
*/
121-
122100
final Set<String> liveNodeIds = liveNodes.stream()
123101
.filter(DiscoveryNode::isMasterNode).map(DiscoveryNode::getId).collect(Collectors.toSet());
124-
final Set<String> liveInConfigIds = new TreeSet<>(currentConfig.getNodeIds());
125-
liveInConfigIds.retainAll(liveNodeIds);
126-
127-
final SortedSet<String> inConfigNotLiveIds = Sets.unmodifiableSortedDifference(currentConfig.getNodeIds(), liveInConfigIds);
128-
final SortedSet<String> nonRetiredInConfigNotLiveIds = new TreeSet<>(inConfigNotLiveIds);
129-
nonRetiredInConfigNotLiveIds.removeAll(retiredNodeIds);
130-
131-
final Set<String> nonRetiredInConfigLiveIds = new TreeSet<>(liveInConfigIds);
132-
nonRetiredInConfigLiveIds.removeAll(retiredNodeIds);
133-
134-
final Set<String> nonRetiredInConfigLiveMasterIds;
135-
final Set<String> nonRetiredInConfigLiveNotMasterIds;
136-
if (nonRetiredInConfigLiveIds.contains(currentMaster.getId())) {
137-
nonRetiredInConfigLiveNotMasterIds = new TreeSet<>(nonRetiredInConfigLiveIds);
138-
nonRetiredInConfigLiveNotMasterIds.remove(currentMaster.getId());
139-
nonRetiredInConfigLiveMasterIds = Collections.singleton(currentMaster.getId());
140-
} else {
141-
nonRetiredInConfigLiveNotMasterIds = nonRetiredInConfigLiveIds;
142-
nonRetiredInConfigLiveMasterIds = Collections.emptySet();
143-
}
144-
145-
final SortedSet<String> nonRetiredLiveNotInConfigIds = Sets.sortedDifference(liveNodeIds, currentConfig.getNodeIds());
146-
nonRetiredLiveNotInConfigIds.removeAll(retiredNodeIds);
102+
final Set<String> currentConfigNodeIds = currentConfig.getNodeIds();
103+
104+
final Set<VotingConfigNode> orderedCandidateNodes = new TreeSet<>();
105+
liveNodes.stream()
106+
.filter(DiscoveryNode::isMasterNode)
107+
.filter(n -> retiredNodeIds.contains(n.getId()) == false)
108+
.forEach(n -> orderedCandidateNodes.add(new VotingConfigNode(n.getId(), true,
109+
n.getId().equals(currentMaster.getId()), currentConfigNodeIds.contains(n.getId()))));
110+
currentConfigNodeIds.stream()
111+
.filter(nid -> liveNodeIds.contains(nid) == false)
112+
.filter(nid -> retiredNodeIds.contains(nid) == false)
113+
.forEach(nid -> orderedCandidateNodes.add(new VotingConfigNode(nid, false, false, true)));
147114

148115
/*
149116
* Now we work out how many nodes should be in the configuration:
150117
*/
151-
final int targetSize;
152-
153-
final int nonRetiredLiveNodeCount = nonRetiredInConfigLiveIds.size() + nonRetiredLiveNotInConfigIds.size();
154-
final int nonRetiredConfigSize = nonRetiredInConfigLiveIds.size() + nonRetiredInConfigNotLiveIds.size();
155-
if (autoShrinkVotingConfiguration) {
156-
if (nonRetiredLiveNodeCount >= 3) {
157-
targetSize = roundDownToOdd(nonRetiredLiveNodeCount);
158-
} else {
159-
// only have one or two available nodes; may not shrink below 3 nodes automatically, but if
160-
// the config (excluding retired nodes) is already smaller than 3 then it's ok.
161-
targetSize = nonRetiredConfigSize < 3 ? 1 : 3;
162-
}
163-
} else {
164-
targetSize = Math.max(roundDownToOdd(nonRetiredLiveNodeCount), nonRetiredConfigSize);
165-
}
118+
final int nonRetiredConfigSize = Math.toIntExact(orderedCandidateNodes.stream().filter(n -> n.inCurrentConfig).count());
119+
final int minimumConfigEnforcedSize = autoShrinkVotingConfiguration ? (nonRetiredConfigSize < 3 ? 1 : 3) : nonRetiredConfigSize;
120+
final int nonRetiredLiveNodeCount = Math.toIntExact(orderedCandidateNodes.stream().filter(n -> n.live).count());
121+
final int targetSize = Math.max(roundDownToOdd(nonRetiredLiveNodeCount), minimumConfigEnforcedSize);
166122

167-
/*
168-
* The new configuration is formed by taking this many nodes in the following preference order:
169-
*/
170123
final VotingConfiguration newConfig = new VotingConfiguration(
171-
// live master first, then other live nodes, preferring the current config, and if we need more then use non-live nodes
172-
Stream.of(nonRetiredInConfigLiveMasterIds, nonRetiredInConfigLiveNotMasterIds, nonRetiredLiveNotInConfigIds,
173-
nonRetiredInConfigNotLiveIds).flatMap(Collection::stream).limit(targetSize).collect(Collectors.toSet()));
124+
orderedCandidateNodes.stream()
125+
.limit(targetSize)
126+
.map(n -> n.id)
127+
.collect(Collectors.toSet()));
174128

129+
// new configuration should have a quorum
175130
if (newConfig.hasQuorum(liveNodeIds)) {
176131
return newConfig;
177132
} else {
178133
// If there are not enough live nodes to form a quorum in the newly-proposed configuration, it's better to do nothing.
179134
return currentConfig;
180135
}
181136
}
137+
138+
static class VotingConfigNode implements Comparable<VotingConfigNode> {
139+
final String id;
140+
final boolean live;
141+
final boolean currentMaster;
142+
final boolean inCurrentConfig;
143+
144+
VotingConfigNode(String id, boolean live, boolean currentMaster, boolean inCurrentConfig) {
145+
this.id = id;
146+
this.live = live;
147+
this.currentMaster = currentMaster;
148+
this.inCurrentConfig = inCurrentConfig;
149+
}
150+
151+
@Override
152+
public int compareTo(VotingConfigNode other) {
153+
// prefer nodes that are live
154+
final int liveComp = Boolean.compare(other.live, live);
155+
if (liveComp != 0) {
156+
return liveComp;
157+
}
158+
// prefer nodes that are in current config for stability
159+
final int inCurrentConfigComp = Boolean.compare(other.inCurrentConfig, inCurrentConfig);
160+
if (inCurrentConfigComp != 0) {
161+
return inCurrentConfigComp;
162+
}
163+
// prefer current master
164+
final int currentMasterComp = Boolean.compare(other.currentMaster, currentMaster);
165+
if (currentMasterComp != 0) {
166+
return currentMasterComp;
167+
}
168+
// tiebreak by node id to have stable ordering
169+
return id.compareTo(other.id);
170+
}
171+
172+
@Override
173+
public String toString() {
174+
return "VotingConfigNode{" +
175+
"id='" + id + '\'' +
176+
", live=" + live +
177+
", currentMaster=" + currentMaster +
178+
", inCurrentConfig=" + inCurrentConfig +
179+
'}';
180+
}
181+
}
182182
}

0 commit comments

Comments
 (0)