summaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2022-11-24 12:10:45 +0100
committerHenning Baldersheim <balder@yahoo-inc.com>2022-11-24 12:10:45 +0100
commit8e2abfc58e936bd6be3194f9e02a21a9b70b7692 (patch)
tree9918c66fc57647c0b1c831223f6b0f9c5d6b680d /container-search/src/main/java/com/yahoo/search/dispatch/searchcluster
parent90b260e83678edc36eb877ec235e0e6ce5892a48 (diff)
Put loadbalancer and invokerfactory in a volatile object to ensure atomic switch when reconfiguring.
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/dispatch/searchcluster')
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/Group.java8
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupListImpl.java38
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java63
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroups.java (renamed from container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupList.java)11
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroupsImpl.java63
5 files changed, 90 insertions, 93 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/Group.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/Group.java
index cf161638104..fbea58054da 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/Group.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/Group.java
@@ -1,8 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.dispatch.searchcluster;
-import com.google.common.collect.ImmutableList;
-
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -22,7 +20,7 @@ public class Group {
private final static int minDocsPerNodeToRequireLowSkew = 100;
private final int id;
- private final ImmutableList<Node> nodes;
+ private final List<Node> nodes;
private final AtomicBoolean hasSufficientCoverage = new AtomicBoolean(true);
private final AtomicBoolean hasFullCoverage = new AtomicBoolean(true);
private final AtomicLong activeDocuments = new AtomicLong(0);
@@ -32,7 +30,7 @@ public class Group {
public Group(int id, List<Node> nodes) {
this.id = id;
- this.nodes = ImmutableList.copyOf(nodes);
+ this.nodes = List.copyOf(nodes);
int idx = 0;
for(var node: nodes) {
@@ -48,7 +46,7 @@ public class Group {
public int id() { return id; }
/** Returns the nodes in this group as an immutable list */
- public ImmutableList<Node> nodes() { return nodes; }
+ public List<Node> nodes() { return nodes; }
/**
* Returns whether this group has sufficient active documents
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupListImpl.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupListImpl.java
deleted file mode 100644
index b0cbf7d1d1d..00000000000
--- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupListImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.yahoo.search.dispatch.searchcluster;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class GroupListImpl implements GroupList {
- private final Map<Integer, Group> groups;
- public GroupListImpl(Map<Integer, Group> groups) {
- this.groups = Map.copyOf(groups);
- }
- @Override public Group group(int id) { return groups.get(id); }
- @Override public Set<Integer> groupKeys() { return groups.keySet();}
- @Override public Collection<Group> groups() { return groups.values(); }
- @Override public int numGroups() { return groups.size(); }
- public static GroupList buildGroupListForTest(int numGroups, int nodesPerGroup) {
- return new GroupListImpl(buildGroupMapForTest(numGroups, nodesPerGroup));
- }
- public static Map<Integer, Group> buildGroupMapForTest(int numGroups, int nodesPerGroup) {
- Map<Integer, Group> groups = new HashMap<>();
- int distributionKey = 0;
- for (int group = 0; group < numGroups; group++) {
- List<Node> groupNodes = new ArrayList<>();
- for (int i = 0; i < nodesPerGroup; i++) {
- Node node = new Node(distributionKey, "host" + distributionKey, group);
- node.setWorking(true);
- groupNodes.add(node);
- distributionKey++;
- }
- Group g = new Group(group, groupNodes);
- groups.put(group, g);
- }
- return Map.copyOf(groups);
- }
-}
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
index bedfa965229..aa3797cb627 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
@@ -2,7 +2,6 @@
package com.yahoo.search.dispatch.searchcluster;
import com.google.common.collect.ImmutableMap;
-import com.google.common.math.Quantiles;
import com.yahoo.container.handler.VipStatus;
import com.yahoo.net.HostName;
import com.yahoo.prelude.Pong;
@@ -14,7 +13,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -24,15 +22,14 @@ import java.util.stream.Collectors;
*
* @author bratseth
*/
-public class SearchCluster implements NodeManager<Node>, GroupList {
+public class SearchCluster implements NodeManager<Node> {
private static final Logger log = Logger.getLogger(SearchCluster.class.getName());
- private final double minActivedocsPercentage;
private final String clusterId;
private final VipStatus vipStatus;
private final PingFactory pingFactory;
- private final GroupList groups;
+ private final SearchGroupsImpl groups;
private long nextLogTime = 0;
/**
@@ -52,12 +49,10 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
}
public SearchCluster(String clusterId, double minActivedocsPercentage, List<Node> nodes,
VipStatus vipStatus, PingFactory pingFactory) {
- this(clusterId, minActivedocsPercentage, toGroups(nodes), vipStatus, pingFactory);
+ this(clusterId, toGroups(nodes, minActivedocsPercentage), vipStatus, pingFactory);
}
- public SearchCluster(String clusterId, double minActivedocsPercentage, GroupList groups,
- VipStatus vipStatus, PingFactory pingFactory) {
+ public SearchCluster(String clusterId, SearchGroupsImpl groups, VipStatus vipStatus, PingFactory pingFactory) {
this.clusterId = clusterId;
- this.minActivedocsPercentage = minActivedocsPercentage;
this.vipStatus = vipStatus;
this.pingFactory = pingFactory;
this.groups = groups;
@@ -74,7 +69,7 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
}
}
- private static Node findLocalCorpusDispatchTarget(String selfHostname, GroupList groups) {
+ private static Node findLocalCorpusDispatchTarget(String selfHostname, SearchGroups groups) {
// A search node in the search cluster in question is configured on the same host as the currently running container.
// It has all the data <==> No other nodes in the search cluster have the same group id as this node.
// That local search node responds.
@@ -86,7 +81,7 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
if (localSearchNodes.size() != 1) return null;
Node localSearchNode = localSearchNodes.iterator().next();
- Group localSearchGroup = groups.group(localSearchNode.group());
+ Group localSearchGroup = groups.get(localSearchNode.group());
// Only use direct dispatch if the local search node has the entire corpus
if (localSearchGroup.nodes().size() != 1) return null;
@@ -100,27 +95,19 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
.toList();
}
- private static GroupList toGroups(Collection<Node> nodes) {
+ private static SearchGroupsImpl toGroups(Collection<Node> nodes, double minActivedocsPercentage) {
ImmutableMap.Builder<Integer, Group> groupsBuilder = new ImmutableMap.Builder<>();
for (Map.Entry<Integer, List<Node>> group : nodes.stream().collect(Collectors.groupingBy(Node::group)).entrySet()) {
Group g = new Group(group.getKey(), group.getValue());
groupsBuilder.put(group.getKey(), g);
}
- return new GroupListImpl(groupsBuilder.build());
+ return new SearchGroupsImpl(groupsBuilder.build(), minActivedocsPercentage);
}
- @Override
- public Group group(int id) {
- return groups.group(id);
- }
- @Override
- public Set<Integer> groupKeys() { return groups.groupKeys(); }
- @Override
- public Collection<Group> groups() { return groups.groups(); }
- @Override
- public int numGroups() { return groups.numGroups(); }
+ public SearchGroups groupList() { return groups; }
+ public Group group(int id) { return groups.get(id); }
- public boolean allGroupsHaveSize1() { return groups().stream().allMatch(g -> g.nodes().size() == 1); }
+ private Collection<Group> groups() { return groups.groups(); }
public int groupsWithSufficientCoverage() {
return (int)groups().stream().filter(Group::hasSufficientCoverage).count();
@@ -134,7 +121,7 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
if ( localCorpusDispatchTarget == null) return Optional.empty();
// Only use direct dispatch if the local group has sufficient coverage
- Group localSearchGroup = group(localCorpusDispatchTarget.group());
+ Group localSearchGroup = groups.get(localCorpusDispatchTarget.group());
if ( ! localSearchGroup.hasSufficientCoverage()) return Optional.empty();
// Only use direct dispatch if the local search node is not down
@@ -225,26 +212,20 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
// With just one group sufficient coverage may not be the same as full coverage, as the
// group will always be marked sufficient for use.
updateSufficientCoverage(group, true);
- boolean sufficientCoverage = isGroupCoverageSufficient(group.activeDocuments(), group.activeDocuments());
+ boolean sufficientCoverage = groups.isGroupCoverageSufficient(group.activeDocuments(), group.activeDocuments());
trackGroupCoverageChanges(group, sufficientCoverage, group.activeDocuments());
}
private void pingIterationCompletedMultipleGroups() {
groups().forEach(Group::aggregateNodeValues);
- long medianDocuments = medianDocumentsPerGroup();
+ long medianDocuments = groups.medianDocumentsPerGroup();
for (Group group : groups()) {
- boolean sufficientCoverage = isGroupCoverageSufficient(group.activeDocuments(), medianDocuments);
+ boolean sufficientCoverage = groups.isGroupCoverageSufficient(group.activeDocuments(), medianDocuments);
updateSufficientCoverage(group, sufficientCoverage);
trackGroupCoverageChanges(group, sufficientCoverage, medianDocuments);
}
}
- private long medianDocumentsPerGroup() {
- if (isEmpty()) return 0;
- var activeDocuments = groups().stream().map(Group::activeDocuments).collect(Collectors.toList());
- return (long)Quantiles.median().compute(activeDocuments);
- }
-
/**
* Update statistics after a round of issuing pings.
* Note that this doesn't wait for pings to return, so it will typically accumulate data from
@@ -252,27 +233,19 @@ public class SearchCluster implements NodeManager<Node>, GroupList {
*/
@Override
public void pingIterationCompleted() {
- if (numGroups() == 1) {
+ if (groups.size() == 1) {
pingIterationCompletedSingleGroup();
} else {
pingIterationCompletedMultipleGroups();
}
}
- private boolean isGroupCoverageSufficient(long activeDocuments, long medianDocuments) {
- double documentCoverage = 100.0 * (double) activeDocuments / medianDocuments;
- return ! (medianDocuments > 0 && documentCoverage < minActivedocsPercentage);
- }
+
/**
* Calculate whether a subset of nodes in a group has enough coverage
*/
- public boolean isPartialGroupCoverageSufficient(List<Node> nodes) {
- if (numGroups() == 1)
- return true;
- long activeDocuments = nodes.stream().mapToLong(Node::getActiveDocuments).sum();
- return isGroupCoverageSufficient(activeDocuments, medianDocumentsPerGroup());
- }
+
private void trackGroupCoverageChanges(Group group, boolean fullCoverage, long medianDocuments) {
if ( ! hasInformationAboutAllNodes()) return; // Be silent until we know what we are talking about.
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupList.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroups.java
index 7d625f55cb6..b041ba28db9 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/GroupList.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroups.java
@@ -7,12 +7,13 @@ import java.util.Set;
* Simple interface for groups and their nodes in the content cluster
* @author baldersheim
*/
-public interface GroupList {
- Group group(int id);
- Set<Integer> groupKeys();
+public interface SearchGroups {
+ Group get(int id);
+ Set<Integer> keys();
Collection<Group> groups();
default boolean isEmpty() {
- return numGroups() == 0;
+ return size() == 0;
}
- int numGroups();
+ int size();
+ boolean isPartialGroupCoverageSufficient(Collection<Node> nodes);
}
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroupsImpl.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroupsImpl.java
new file mode 100644
index 00000000000..a8f0d07b494
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchGroupsImpl.java
@@ -0,0 +1,63 @@
+package com.yahoo.search.dispatch.searchcluster;
+
+import com.google.common.math.Quantiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class SearchGroupsImpl implements SearchGroups {
+ private final Map<Integer, Group> groups;
+ private final double minActivedocsPercentage;
+ public SearchGroupsImpl(Map<Integer, Group> groups, double minActivedocsPercentage) {
+ this.groups = Map.copyOf(groups);
+ this.minActivedocsPercentage = minActivedocsPercentage;
+ }
+ @Override public Group get(int id) { return groups.get(id); }
+ @Override public Set<Integer> keys() { return groups.keySet();}
+ @Override public Collection<Group> groups() { return groups.values(); }
+ @Override public int size() { return groups.size(); }
+ @Override
+ public boolean isPartialGroupCoverageSufficient(Collection<Node> nodes) {
+ if (size() == 1)
+ return true;
+ long activeDocuments = nodes.stream().mapToLong(Node::getActiveDocuments).sum();
+ return isGroupCoverageSufficient(activeDocuments, medianDocumentsPerGroup());
+ }
+
+ public boolean isGroupCoverageSufficient(long activeDocuments, long medianDocuments) {
+ double documentCoverage = 100.0 * (double) activeDocuments / medianDocuments;
+ return ! (medianDocuments > 0 && documentCoverage < minActivedocsPercentage);
+ }
+
+ public long medianDocumentsPerGroup() {
+ if (isEmpty()) return 0;
+ var activeDocuments = groups().stream().map(Group::activeDocuments).collect(Collectors.toList());
+ return (long) Quantiles.median().compute(activeDocuments);
+ }
+
+
+ public static SearchGroupsImpl buildGroupListForTest(int numGroups, int nodesPerGroup, double minActivedocsPercentage) {
+ return new SearchGroupsImpl(buildGroupMapForTest(numGroups, nodesPerGroup), minActivedocsPercentage);
+ }
+ public static Map<Integer, Group> buildGroupMapForTest(int numGroups, int nodesPerGroup) {
+ Map<Integer, Group> groups = new HashMap<>();
+ int distributionKey = 0;
+ for (int group = 0; group < numGroups; group++) {
+ List<Node> groupNodes = new ArrayList<>();
+ for (int i = 0; i < nodesPerGroup; i++) {
+ Node node = new Node(distributionKey, "host" + distributionKey, group);
+ node.setWorking(true);
+ groupNodes.add(node);
+ distributionKey++;
+ }
+ Group g = new Group(group, groupNodes);
+ groups.put(group, g);
+ }
+ return Map.copyOf(groups);
+ }
+}