summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorOlli Virtanen <olli.virtanen@oath.com>2018-09-06 14:40:30 +0200
committerOlli Virtanen <olli.virtanen@oath.com>2018-09-06 14:40:30 +0200
commitcf657507ef1b2e83d6b38bc0914d9faa56d0a143 (patch)
tree76faa36d50c8983ab38933ee7fe6aac734bb23fe /container-search
parent1d1fa442fa5039c0a17e3b2e900ad7adc76e673b (diff)
Added copyrights and javadocs. Logic and naming in LB cleaned up
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/CloseableChannel.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/DispatchedChannel.java6
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/LoadBalancer.java56
-rw-r--r--container-search/src/test/java/com/yahoo/search/dispatch/LoadBalancerTest.java21
5 files changed, 65 insertions, 24 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/CloseableChannel.java b/container-search/src/main/java/com/yahoo/search/dispatch/CloseableChannel.java
index 9a3e7e71031..838afa0c7fc 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/CloseableChannel.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/CloseableChannel.java
@@ -1,3 +1,4 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.dispatch;
import com.yahoo.fs4.BasicPacket;
@@ -11,6 +12,9 @@ import java.io.Closeable;
import java.io.IOException;
import java.util.Optional;
+/**
+ * @author ollivir
+ */
public class CloseableChannel implements Closeable {
private FS4Channel channel;
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/DispatchedChannel.java b/container-search/src/main/java/com/yahoo/search/dispatch/DispatchedChannel.java
index 85926845647..00c59fbc979 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/DispatchedChannel.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/DispatchedChannel.java
@@ -1,3 +1,4 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.dispatch;
import com.yahoo.prelude.fastsearch.FS4ResourcePool;
@@ -6,6 +7,11 @@ import com.yahoo.search.dispatch.SearchCluster.Node;
import java.util.Optional;
+/**
+ * An extension to CloseableChannel that encapsulates the release of a LoadBalancer group allocation.
+ *
+ * @author ollivir
+ */
public class DispatchedChannel extends CloseableChannel {
private final SearchCluster.Group group;
private final LoadBalancer loadBalancer;
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
index f3677fd8d8d..be7cfea2017 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
@@ -284,7 +284,7 @@ public class Dispatcher extends AbstractComponent {
}
public Optional<CloseableChannel> getDispatchedChannel(Query query) {
- Optional<SearchCluster.Group> groupInCluster = loadBalancer.getGroupForQuery(query);
+ Optional<SearchCluster.Group> groupInCluster = loadBalancer.takeGroupForQuery(query);
return groupInCluster.flatMap(group -> {
if(group.nodes().size() == 1) {
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 652ee134a87..d8e12980472 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
@@ -1,3 +1,4 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.dispatch;
import com.yahoo.search.Query;
@@ -10,6 +11,12 @@ import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
+/**
+ * LoadBalancer determines which group of content nodes should be accessed next for each search query when the internal java dispatcher is
+ * used.
+ *
+ * @author ollivir
+ */
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
@@ -35,7 +42,14 @@ public class LoadBalancer {
Collections.shuffle(scoreboard);
}
- public Optional<Group> getGroupForQuery(Query query) {
+ /**
+ * Select and allocate the search cluster group which is to be used for the provided query. Callers <b>must</b> call
+ * {@link #releaseGroup(Group)} symmetrically for each taken allocation.
+ *
+ * @param query
+ * @return The node group to target, or <i>empty</i> if the internal dispatch logic cannot be used
+ */
+ public Optional<Group> takeGroupForQuery(Query query) {
if (!isInternallyDispatchable) {
return Optional.empty();
}
@@ -43,6 +57,12 @@ public class LoadBalancer {
return allocateNextGroup();
}
+ /**
+ * Release an allocation given by {@link #takeGroupForQuery(Query)}. The release must be done exactly once for each allocation.
+ *
+ * @param group
+ * previously allocated group
+ */
public void releaseGroup(Group group) {
synchronized (this) {
for (GroupSchedule sched : scoreboard) {
@@ -61,18 +81,13 @@ public class LoadBalancer {
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)) {
+ if (sched.isPreferredOver(bestSchedule)) {
bestSchedule = sched;
}
+ index = nextScoreboardIndex(index);
}
- needle++;
- if (needle >= scoreboard.size()) {
- needle = 0;
- }
+ needle = nextScoreboardIndex(needle);
+
Group ret = null;
if (bestSchedule != null) {
bestSchedule.adjustScore(1);
@@ -85,7 +100,15 @@ public class LoadBalancer {
}
}
- public static class GroupSchedule implements Comparable<GroupSchedule> {
+ private int nextScoreboardIndex(int current) {
+ int next = current + 1;
+ if (next >= scoreboard.size()) {
+ next %= scoreboard.size();
+ }
+ return next;
+ }
+
+ private static class GroupSchedule {
private final Group group;
private int score;
@@ -94,9 +117,14 @@ public class LoadBalancer {
this.score = 0;
}
- @Override
- public int compareTo(GroupSchedule that) {
- return this.score - that.score;
+ public boolean isPreferredOver(GroupSchedule other) {
+ if (! group.hasSufficientCoverage()) {
+ return false;
+ }
+ if (other == null) {
+ return true;
+ }
+ return this.score < other.score;
}
public void adjustScore(int amount) {
diff --git a/container-search/src/test/java/com/yahoo/search/dispatch/LoadBalancerTest.java b/container-search/src/test/java/com/yahoo/search/dispatch/LoadBalancerTest.java
index 443ab52d8cc..2ba991310f5 100644
--- a/container-search/src/test/java/com/yahoo/search/dispatch/LoadBalancerTest.java
+++ b/container-search/src/test/java/com/yahoo/search/dispatch/LoadBalancerTest.java
@@ -14,6 +14,9 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
+/**
+ * @author ollivir
+ */
public class LoadBalancerTest {
@Test
public void requreThatLoadBalancerServesSingleNodeSetups() {
@@ -21,7 +24,7 @@ public class LoadBalancerTest {
SearchCluster cluster = new SearchCluster(88.0, Arrays.asList(n1), null, 1, null);
LoadBalancer lb = new LoadBalancer(cluster);
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
Group group = grp.orElseGet(() -> {
throw new AssertionFailedError("Expected a SearchCluster.Group");
});
@@ -35,7 +38,7 @@ public class LoadBalancerTest {
SearchCluster cluster = new SearchCluster(88.0, Arrays.asList(n1, n2), null, 1, null);
LoadBalancer lb = new LoadBalancer(cluster);
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
Group group = grp.orElseGet(() -> {
throw new AssertionFailedError("Expected a SearchCluster.Group");
});
@@ -49,7 +52,7 @@ public class LoadBalancerTest {
SearchCluster cluster = new SearchCluster(88.0, Arrays.asList(n1, n2), null, 2, null);
LoadBalancer lb = new LoadBalancer(cluster);
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
assertThat(grp.isPresent(), is(false));
}
@@ -62,7 +65,7 @@ public class LoadBalancerTest {
SearchCluster cluster = new SearchCluster(88.0, Arrays.asList(n1, n2, n3, n4), null, 2, null);
LoadBalancer lb = new LoadBalancer(cluster);
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
assertThat(grp.isPresent(), is(false));
}
@@ -74,14 +77,14 @@ public class LoadBalancerTest {
LoadBalancer lb = new LoadBalancer(cluster);
// get first group
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
Group group = grp.get();
int id1 = group.id();
// release allocation
lb.releaseGroup(group);
// get second group
- grp = lb.getGroupForQuery(null);
+ grp = lb.takeGroupForQuery(null);
group = grp.get();
assertThat(group.id(), not(equalTo(id1)));
}
@@ -94,12 +97,12 @@ public class LoadBalancerTest {
LoadBalancer lb = new LoadBalancer(cluster);
// get first group
- Optional<Group> grp = lb.getGroupForQuery(null);
+ Optional<Group> grp = lb.takeGroupForQuery(null);
Group group = grp.get();
int id1 = group.id();
// get second group
- grp = lb.getGroupForQuery(null);
+ grp = lb.takeGroupForQuery(null);
group = grp.get();
int id2 = group.id();
assertThat(id2, not(equalTo(id1)));
@@ -107,7 +110,7 @@ public class LoadBalancerTest {
lb.releaseGroup(group);
// get third group
- grp = lb.getGroupForQuery(null);
+ grp = lb.takeGroupForQuery(null);
group = grp.get();
assertThat(group.id(), equalTo(id2));
}