summaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java
diff options
context:
space:
mode:
authorOlli Virtanen <olli.virtanen@oath.com>2018-09-06 13:04:12 +0200
committerOlli Virtanen <olli.virtanen@oath.com>2018-09-06 13:04:12 +0200
commit1d1fa442fa5039c0a17e3b2e900ad7adc76e673b (patch)
treef716ac55c7dd422031840bbce41c71a61328cd10 /container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java
parentc1fdecf3cb26f1a3aef2caf290916a4f533c6c58 (diff)
Java dispatch support for multiple groups of single nodes
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java')
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java99
1 files changed, 91 insertions, 8 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java b/container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java
index 8e90eae0eb3..652ee134a87 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java
@@ -3,25 +3,108 @@ package com.yahoo.search.dispatch;
import com.yahoo.search.Query;
import com.yahoo.search.dispatch.SearchCluster.Group;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Optional;
+import java.util.logging.Level;
+import java.util.logging.Logger;
public class LoadBalancer {
+ // The implementation here is a simplistic least queries in flight + round-robin load balancer
+ // TODO: consider the options in com.yahoo.vespa.model.content.TuningDispatch
- private final SearchCluster searchCluster;
+ private final static Logger log = Logger.getLogger(LoadBalancer.class.getName());
+
+ private final boolean isInternallyDispatchable;
+ private final List<GroupSchedule> scoreboard;
+ private int needle = 0;
public LoadBalancer(SearchCluster searchCluster) {
- this.searchCluster = searchCluster;
+ if (searchCluster == null) {
+ this.isInternallyDispatchable = false;
+ this.scoreboard = null;
+ return;
+ }
+ this.isInternallyDispatchable = (searchCluster.groupSize() == 1);
+ this.scoreboard = new ArrayList<>(searchCluster.groups().size());
+
+ for (Group group : searchCluster.groups().values()) {
+ scoreboard.add(new GroupSchedule(group));
+ }
+ Collections.shuffle(scoreboard);
}
public Optional<Group> getGroupForQuery(Query query) {
- if (searchCluster.groups().size() == 1) {
- for(Group group: searchCluster.groups().values()) {
- // since the number of groups is 1, this will run only once
- if(group.nodes().size() == 1) {
- return Optional.of(group);
+ if (!isInternallyDispatchable) {
+ return Optional.empty();
+ }
+
+ return allocateNextGroup();
+ }
+
+ public void releaseGroup(Group group) {
+ synchronized (this) {
+ for (GroupSchedule sched : scoreboard) {
+ if (sched.group.id() == group.id()) {
+ sched.adjustScore(-1);
+ break;
+ }
+ }
+ }
+ }
+
+ private Optional<Group> allocateNextGroup() {
+ synchronized (this) {
+ GroupSchedule bestSchedule = null;
+
+ int index = needle;
+ for (int i = 0; i < scoreboard.size(); i++) {
+ GroupSchedule sched = scoreboard.get(index);
+ index++;
+ if (index >= scoreboard.size()) {
+ index = 0;
}
+ if (sched.group.hasSufficientCoverage() && (bestSchedule == null || sched.compareTo(bestSchedule) < 0)) {
+ bestSchedule = sched;
+ }
+ }
+ needle++;
+ if (needle >= scoreboard.size()) {
+ needle = 0;
+ }
+ Group ret = null;
+ if (bestSchedule != null) {
+ bestSchedule.adjustScore(1);
+ ret = bestSchedule.group;
+ }
+ if (log.isLoggable(Level.FINE)) {
+ log.fine("Offering <" + ret + "> for query connection");
+ }
+ return Optional.ofNullable(ret);
+ }
+ }
+
+ public static class GroupSchedule implements Comparable<GroupSchedule> {
+ private final Group group;
+ private int score;
+
+ public GroupSchedule(Group group) {
+ this.group = group;
+ this.score = 0;
+ }
+
+ @Override
+ public int compareTo(GroupSchedule that) {
+ return this.score - that.score;
+ }
+
+ public void adjustScore(int amount) {
+ this.score += amount;
+ if (score < 0) {
+ log.warning("Double free of query target group detected");
+ score = 0;
}
}
- return Optional.empty();
}
}