summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorOlli Virtanen <olli.virtanen@oath.com>2018-10-11 11:30:23 +0200
committerOlli Virtanen <olli.virtanen@oath.com>2018-10-11 11:30:23 +0200
commit706a869002c4e2eb6e1d7896a523703aeca68028 (patch)
tree2902167ffa2d13a01da80dc1492b2823a0012f5f /container-search
parent81b380935d3412c29ad3f84e3c8f88361851d0d1 (diff)
Rename part->node, row->group; store group introduction order in SearchCluster
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java26
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/SearchPath.java125
-rw-r--r--container-search/src/test/java/com/yahoo/search/dispatch/MockSearchCluster.java8
3 files changed, 92 insertions, 67 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java b/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
index 48ddba6c301..a0c7447fd3e 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/SearchCluster.java
@@ -19,6 +19,7 @@ import com.yahoo.yolean.Exceptions;
import com.yahoo.prelude.Pong;
import com.yahoo.prelude.fastsearch.FS4ResourcePool;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -49,6 +50,7 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
private final int size;
private final ImmutableMap<Integer, Group> groups;
private final ImmutableMultimap<String, Node> nodesByHost;
+ private final ImmutableList<Group> orderedGroups;
private final ClusterMonitor<Node> clusterMonitor;
private final VipStatus vipStatus;
@@ -80,9 +82,14 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
// Create groups
ImmutableMap.Builder<Integer, Group> groupsBuilder = new ImmutableMap.Builder<>();
- for (Map.Entry<Integer, List<Node>> group : nodes.stream().collect(Collectors.groupingBy(Node::group)).entrySet())
- groupsBuilder.put(group.getKey(), new Group(group.getKey(), group.getValue()));
+ 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);
+ }
this.groups = groupsBuilder.build();
+ LinkedHashMap<Integer, Group> groupIntroductionOrder = new LinkedHashMap<>();
+ nodes.forEach(node -> groupIntroductionOrder.put(node.group(), groups.get(node.group)));
+ this.orderedGroups = ImmutableList.<Group>builder().addAll(groupIntroductionOrder.values()).build();
// Index nodes by host
ImmutableMultimap.Builder<String, Node> nodesByHostBuilder = new ImmutableMultimap.Builder<>();
@@ -146,12 +153,21 @@ public class SearchCluster implements NodeManager<SearchCluster.Node> {
/** Returns the groups of this cluster as an immutable map indexed by group id */
public ImmutableMap<Integer, Group> groups() { return groups; }
+ /** Returns the n'th (zero-indexed) group in the cluster if possible */
+ public Optional<Group> group(int n) {
+ if (orderedGroups.size() < n) {
+ return Optional.of(orderedGroups.get(n));
+ } else {
+ return Optional.empty();
+ }
+ }
+
/** Returns the number of nodes per group - size()/groups.size() */
- public int groupSize() {
+ public int groupSize() {
if (groups.size() == 0) return size();
- return size() / groups.size();
+ return size() / groups.size();
}
-
+
/**
* Returns the nodes of this cluster as an immutable map indexed by host.
* One host may contain multiple nodes (on different ports), so this is a multi-map.
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/SearchPath.java b/container-search/src/main/java/com/yahoo/search/dispatch/SearchPath.java
index ede6725aba2..57f06225d27 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/SearchPath.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/SearchPath.java
@@ -1,14 +1,13 @@
// 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.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableCollection;
import com.yahoo.collections.Pair;
import com.yahoo.search.dispatch.SearchCluster.Group;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -62,16 +61,16 @@ public class SearchPath {
return Optional.of(sp);
}
} catch (NumberFormatException | InvalidSearchPathException e) {
- throw new InvalidSearchPathException("Invalid search path: " + path);
+ throw new InvalidSearchPathException("Invalid search path: " + path, e);
}
}
- private final List<Part> parts;
- private final Integer row;
+ private final List<NodeSelection> nodes;
+ private final Integer group;
- private SearchPath(List<Part> parts, Integer row) {
- this.parts = parts;
- this.row = row;
+ private SearchPath(List<NodeSelection> nodes, Integer group) {
+ this.nodes = nodes;
+ this.group = group;
}
private List<SearchCluster.Node> mapToNodes(SearchCluster cluster) {
@@ -79,116 +78,114 @@ public class SearchPath {
return Collections.emptyList();
}
- SearchCluster.Group group = selectGroup(cluster);
+ SearchCluster.Group selectedGroup = selectGroup(cluster);
- if (parts.isEmpty()) {
- return group.nodes();
+ if (nodes.isEmpty()) {
+ return selectedGroup.nodes();
}
+ List<SearchCluster.Node> groupNodes = selectedGroup.nodes();
Set<Integer> wanted = new HashSet<>();
- int max = group.nodes().size();
- for (Part part : parts) {
- wanted.addAll(part.matches(max));
+ int max = groupNodes.size();
+ for (NodeSelection node : nodes) {
+ wanted.addAll(node.matches(max));
}
- // ordering by distribution key might not be equal to ordering in services.xml
- List<SearchCluster.Node> sortedByDistKey = new ArrayList<>(group.nodes());
- sortedByDistKey.sort(Comparator.comparingInt(SearchCluster.Node::key));
-
List<SearchCluster.Node> ret = new ArrayList<>();
for (int idx : wanted) {
- ret.add(sortedByDistKey.get(idx));
+ ret.add(groupNodes.get(idx));
}
return ret;
}
private boolean isEmpty() {
- return parts.isEmpty() && row == null;
+ return nodes.isEmpty() && group == null;
}
private Group selectGroup(SearchCluster cluster) {
- // ordering by group id might not be equal to ordering in services.xml
- ImmutableMap<Integer, SearchCluster.Group> byId = cluster.groups();
- List<Integer> sortedKeys = new ArrayList<>(byId.keySet());
- Collections.sort(sortedKeys);
-
- if (row != null && row < sortedKeys.size()) {
- return byId.get(sortedKeys.get(row));
+ if (group != null) {
+ Optional<Group> specificGroup = cluster.group(group);
+ if (specificGroup.isPresent()) {
+ return specificGroup.get();
+ } else {
+ throw new InvalidSearchPathException("Invalid searchPath, cluster does not have " + (group + 1) + " groups");
+ }
}
// pick "anything": try to find the first working
- for (Integer id : sortedKeys) {
- SearchCluster.Group g = byId.get(id);
+ ImmutableCollection<Group> groups = cluster.groups().values();
+ for (Group g : groups) {
if (g.hasSufficientCoverage()) {
return g;
}
}
+
// fallback: first
- return byId.get(sortedKeys.get(0));
+ return groups.iterator().next();
}
private static SearchPath parseElement(String element) {
- Pair<String, String> partAndRow = halveAt('/', element);
- List<Part> parts = parseParts(partAndRow.getFirst());
- Integer row = parseRow(partAndRow.getSecond());
+ Pair<String, String> nodesAndGroup = halveAt('/', element);
+ List<NodeSelection> nodes = parseNodes(nodesAndGroup.getFirst());
+ Integer group = parseGroup(nodesAndGroup.getSecond());
- return new SearchPath(parts, row);
+ return new SearchPath(nodes, group);
}
- private static List<Part> parseParts(String parts) {
- List<Part> ret = new ArrayList<>();
- while (parts.length() > 0) {
- if (parts.startsWith("[")) {
- parts = parsePartRange(parts, ret);
+ private static List<NodeSelection> parseNodes(String nodes) {
+ List<NodeSelection> ret = new ArrayList<>();
+ while (nodes.length() > 0) {
+ if (nodes.startsWith("[")) {
+ nodes = parseNodeRange(nodes, ret);
} else {
- if (isWildcard(parts)) { // any part will be accepted
+ if (isWildcard(nodes)) { // any node will be accepted
return Collections.emptyList();
}
- parts = parsePartNum(parts, ret);
+ nodes = parseNodeNum(nodes, ret);
}
}
return ret;
}
// an asterisk or an empty string followed by a comma or the end of the string
- private static final Pattern WILDCARD_PART = Pattern.compile("^\\*?(?:,|$)");
+ private static final Pattern NODE_WILDCARD = Pattern.compile("^\\*?(?:,|$)");
- private static boolean isWildcard(String part) {
- return WILDCARD_PART.matcher(part).lookingAt();
+ private static boolean isWildcard(String node) {
+ return NODE_WILDCARD.matcher(node).lookingAt();
}
- private static final Pattern PART_RANGE = Pattern.compile("^\\[(\\d+),(\\d+)>(?:,|$)");
+ private static final Pattern NODE_RANGE = Pattern.compile("^\\[(\\d+),(\\d+)>(?:,|$)");
- private static String parsePartRange(String parts, List<Part> into) {
- Matcher m = PART_RANGE.matcher(parts);
+ private static String parseNodeRange(String nodes, List<NodeSelection> into) {
+ Matcher m = NODE_RANGE.matcher(nodes);
if (m.find()) {
- String ret = parts.substring(m.end());
+ String ret = nodes.substring(m.end());
Integer start = Integer.parseInt(m.group(1));
Integer end = Integer.parseInt(m.group(2));
if (start > end) {
throw new InvalidSearchPathException("Invalid range");
}
- into.add(new Part(start, end));
+ into.add(new NodeSelection(start, end));
return ret;
} else {
throw new InvalidSearchPathException("Invalid range expression");
}
}
- private static String parsePartNum(String parts, List<Part> into) {
- Pair<String, String> numAndRest = halveAt(',', parts);
- int partNum = Integer.parseInt(numAndRest.getFirst());
- into.add(new Part(partNum, partNum + 1));
+ private static String parseNodeNum(String nodes, List<NodeSelection> into) {
+ Pair<String, String> numAndRest = halveAt(',', nodes);
+ int nodeNum = Integer.parseInt(numAndRest.getFirst());
+ into.add(new NodeSelection(nodeNum, nodeNum + 1));
return numAndRest.getSecond();
}
- private static Integer parseRow(String row) {
- if (row.isEmpty()) {
+ private static Integer parseGroup(String group) {
+ if (group.isEmpty()) {
return null;
}
- if ("/".equals(row) || "*".equals(row)) { // anything goes
+ if ("/".equals(group) || "*".equals(group)) { // anything goes
return null;
}
- return Integer.parseInt(row);
+ return Integer.parseInt(group);
}
private static Pair<String, String> halveAt(char divider, String string) {
@@ -203,7 +200,7 @@ public class SearchPath {
public String toString() {
StringBuilder sb = new StringBuilder();
boolean first = true;
- for (Part p : parts) {
+ for (NodeSelection p : nodes) {
if (first) {
first = false;
} else {
@@ -211,17 +208,17 @@ public class SearchPath {
}
sb.append(p.toString());
}
- if (row != null) {
- sb.append('/').append(row);
+ if (group != null) {
+ sb.append('/').append(group);
}
return sb.toString();
}
- private static class Part {
+ private static class NodeSelection {
private final int from;
private final int to;
- Part(int from, int to) {
+ NodeSelection(int from, int to) {
this.from = from;
this.to = to;
}
@@ -248,6 +245,10 @@ public class SearchPath {
public InvalidSearchPathException(String message) {
super(message);
}
+
+ public InvalidSearchPathException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
}
diff --git a/container-search/src/test/java/com/yahoo/search/dispatch/MockSearchCluster.java b/container-search/src/test/java/com/yahoo/search/dispatch/MockSearchCluster.java
index 3218f4bac16..89b416f3293 100644
--- a/container-search/src/test/java/com/yahoo/search/dispatch/MockSearchCluster.java
+++ b/container-search/src/test/java/com/yahoo/search/dispatch/MockSearchCluster.java
@@ -54,6 +54,14 @@ public class MockSearchCluster extends SearchCluster {
return numNodesPerGroup;
}
+ public Optional<Group> group(int n) {
+ if(n < numGroups) {
+ return Optional.of(groups.get(n));
+ } else {
+ return Optional.empty();
+ }
+ }
+
public ImmutableMultimap<String, Node> nodesByHost() {
return nodesByHost;
}