tflobbe commented on code in PR #1577:
URL: https://github.com/apache/solr/pull/1577#discussion_r1172989171
##########
solr/core/src/java/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactory.java:
##########
@@ -503,15 +534,199 @@ private String getNodeAZ(Node n, final AttributeValues
attrValues) {
*/
private static class AzWithNodes {
final String azName;
- List<Node> availableNodesForPlacement;
- boolean hasBeenSorted;
-
- AzWithNodes(String azName, List<Node> availableNodesForPlacement) {
+ private final boolean useAntiAffinity;
+ private boolean listIsSorted = false;
+ private final Comparator<Node> nodeComparator;
+ private final Random random;
+ private final List<Node> availableNodesForPlacement;
+ private final AttributeValues attributeValues;
+ private TreeSet<AffinityGroupWithNodes> sortedAntiAffinityGroups;
+ private final Map<String, Integer> currentAntiAffinityUsage;
+ private int numNodesForPlacement;
+
+ AzWithNodes(
+ String azName,
+ List<Node> availableNodesForPlacement,
+ boolean useAntiAffinity,
+ Comparator<Node> nodeComparator,
+ Random random,
+ AttributeValues attributeValues,
+ Map<String, Integer> currentAntiAffinityUsage) {
this.azName = azName;
this.availableNodesForPlacement = availableNodesForPlacement;
- // Once the list is sorted to an order we're happy with, this flag is
set to true to avoid
- // sorting multiple times unnecessarily.
- this.hasBeenSorted = false;
+ this.useAntiAffinity = useAntiAffinity;
+ this.nodeComparator = nodeComparator;
+ this.random = random;
+ this.attributeValues = attributeValues;
+ this.currentAntiAffinityUsage = currentAntiAffinityUsage;
+ this.numNodesForPlacement = availableNodesForPlacement.size();
+ }
+
+ private boolean hasBeenSorted() {
+ return (useAntiAffinity && sortedAntiAffinityGroups != null)
+ || (!useAntiAffinity && listIsSorted);
+ }
+
+ void ensureSorted() {
+ if (!hasBeenSorted()) {
+ sort();
+ }
+ }
+
+ private void sort() {
+ assert !listIsSorted && sortedAntiAffinityGroups == null
+ : "We shouldn't be sorting this list again";
+
+ // Make sure we do not tend to use always the same nodes (within an
AZ) if all
+ // conditions are identical (well, this likely is not the case since
after having added
+ // a replica to a node its number of cores increases for the next
placement decision,
+ // but let's be defensive here, given that multiple concurrent
placement decisions might
+ // see the same initial cluster state, and we want placement to be
reasonable even in
+ // that case without creating an unnecessary imbalance). For example,
if all nodes have
+ // 0 cores and same amount of free disk space, ideally we want to pick
a random node for
+ // placement, not always the same one due to some internal ordering.
+ Collections.shuffle(availableNodesForPlacement, random);
+
+ if (useAntiAffinity) {
+ // When we use anti-affinity, we don't just sort the list of nodes,
instead we generate a
+ // TreeSet of AffinityGroupWithNodes,
+ // sorted by the number of times the affinity label has been used.
Each
+ // AffinityGroupWithNodes internally contains the list of nodes that
use a particular
+ // affinity
+ // label, and it's sorted internally by the comparator passed to this
+ // class (which is the same that's used when not using
anti-affinity).
+ // Whenever a node from a particular AffinityGroupWithNodes is
selected as the best
+ // candidate, the call to "removeBestNode" will:
+ // 1. Remove the AffinityGroupWithNodes instance from the TreeSet
+ // 2. Remove the best node from the list within the
AffinityGroupWithNodes
+ // 3. Increment the count of times the affinity label has been used
+ // 4. Re-add the AffinityGroupWithNodes instance to the TreeSet if
there are still nodes
+ // available
+ HashMap<String, List<Node>> antiAffinityNameToListOfNodesMap = new
HashMap<>();
+ for (Node node : availableNodesForPlacement) {
+ antiAffinityNameToListOfNodesMap.compute(
+ attributeValues
+ .getSystemProperty(node,
AffinityPlacementConfig.ANTI_AFFINITY_SYSPROP)
+ .get(),
+ (k, v) -> {
+ if (v == null) {
+ v = new ArrayList<>();
+ }
+ v.add(node);
+ return v;
+ });
+ }
+ sortedAntiAffinityGroups =
+ new TreeSet<>(new
AntiAffinityComparator(currentAntiAffinityUsage));
+
+ int i = 0;
+ for (Map.Entry<String, List<Node>> entry :
antiAffinityNameToListOfNodesMap.entrySet()) {
+ // Sort the nodes within the anti-affinity group by the provided
comparator
+ entry.getValue().sort(nodeComparator);
+ sortedAntiAffinityGroups.add(
+ new AffinityGroupWithNodes(entry.getKey(), entry.getValue(),
i++, nodeComparator));
+ }
+ } else {
+ availableNodesForPlacement.sort(nodeComparator);
+ listIsSorted = true;
+ }
+ }
+
+ Node getBestNode() {
+ assert hasBeenSorted();
Review Comment:
There isn't, for now. The reason I decided to have the assert is that the
API of this class relies on the class being correctly used, in the correct
order, etc. I want to protect against future changes that may start using this
class incorrectly
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]