summaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2019-08-02 15:58:24 +0200
committerHenning Baldersheim <balder@yahoo-inc.com>2019-08-02 15:58:24 +0200
commit19258aabe2bbf2dc506187cd652293b31351f465 (patch)
tree41fd7e41be65b7c05475c8ec76ad208f38fe4656 /config-model
parent4a5b16e669ee575e163fccc35fd76d6c7ce3378a (diff)
As redundancy and searchable copies depends on group setup normally,
but in hosted it is within a group, we have had some very dirty and incorrect code for reasoning here. Now this is taken care of at constrution of the Redundancy object, so that use is identical later on.
Diffstat (limited to 'config-model')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java38
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java48
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java84
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java32
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java24
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java2
11 files changed, 116 insertions, 131 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
index b324b496b1a..a6228af8991 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
@@ -233,7 +233,7 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
Optional<Tuning> tuning = Optional.ofNullable(this.tuning);
if (element == null) {
snode = SearchNode.create(parent, "" + node.getDistributionKey(), node.getDistributionKey(), spec,
- clusterName, node, flushOnShutdown, tuning, parentGroup.getOwner().isHostedVespa());
+ clusterName, node, flushOnShutdown, tuning, parentGroup.isHosted());
snode.setHostResource(node.getHostResource());
snode.initService(deployState.getDeployLogger());
@@ -283,12 +283,12 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
if (usesHierarchicDistribution()) {
indexedCluster.setMaxNodesDownPerFixedRow((redundancy.effectiveFinalRedundancy() / groupToSpecMap.size()) - 1);
}
- indexedCluster.setSearchableCopies(redundancy.searchableCopies());
+ indexedCluster.setSearchableCopies(redundancy.readyCopies());
}
this.redundancy = redundancy;
for (SearchNode node : getSearchNodes()) {
- node.setRedundancy(redundancy.redundancyFromSearchNodePerspective());
- node.setSearchableCopies(redundancy.searchableCopies());
+ node.setRedundancy(redundancy.finalRedundancy());
+ node.setSearchableCopies(redundancy.readyCopies());
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
index c721d3d48bc..7ab5ee9fd80 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
@@ -33,15 +33,15 @@ public class IndexedHierarchicDistributionValidator {
public void validate() throws Exception {
validateThatWeHaveOneGroupLevel();
validateThatLeafGroupsHasEqualNumberOfNodes();
- validateThatLeafGroupsCountIsAFactorOfRedundancy();
+ validateThatLeafGroupsCountIsAFactorOfRedundancy(clusterName, redundancy.effectiveFinalRedundancy(), rootGroup.getSubgroups().size());
validateThatRedundancyPerGroupIsEqual();
- validateThatReadyCopiesIsCompatibleWithRedundancy(rootGroup.getSubgroups().size());
+ validateThatReadyCopiesIsCompatibleWithRedundancy(clusterName, redundancy.effectiveFinalRedundancy(), redundancy.effectiveReadyCopies(), rootGroup.getSubgroups().size());
}
private void validateThatWeHaveOneGroupLevel() {
for (StorageGroup group : rootGroup.getSubgroups()) {
if (group.getSubgroups().size() > 0) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected all groups under root group '" +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected all groups under root group '" +
rootGroup.getName() + "' to be leaf groups only containing nodes, but sub group '" + group.getName() + "' contains " +
group.getSubgroups().size() + " sub groups.");
}
@@ -59,18 +59,18 @@ public class IndexedHierarchicDistributionValidator {
}
if (group.getNodes().size() != previousGroup.getNodes().size())
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected leaf groups to contain an equal number of nodes, but leaf group '" +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected leaf groups to contain an equal number of nodes, but leaf group '" +
previousGroup.getName() + "' contains " + previousGroup.getNodes().size() + " node(s) while leaf group '" +
group.getName() + "' contains " + group.getNodes().size() + " node(s).");
previousGroup = group;
}
}
- private void validateThatLeafGroupsCountIsAFactorOfRedundancy() {
- if (redundancy.effectiveFinalRedundancy() % rootGroup.getSubgroups().size() != 0) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected number of leaf groups (" +
- rootGroup.getSubgroups().size() + ") to be a factor of redundancy (" +
- redundancy.effectiveFinalRedundancy() + "), but it is not.");
+ static public void validateThatLeafGroupsCountIsAFactorOfRedundancy(String clusterName, int totalRedundancy, int subGroups) {
+ if (totalRedundancy % subGroups != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected number of leaf groups (" +
+ subGroups + ") to be a factor of redundancy (" +
+ totalRedundancy + "), but it is not.");
}
}
@@ -78,7 +78,7 @@ public class IndexedHierarchicDistributionValidator {
int redundancyPerGroup = redundancy.effectiveFinalRedundancy() / rootGroup.getSubgroups().size();
String expPartitions = createDistributionPartitions(redundancyPerGroup, rootGroup.getSubgroups().size());
if (!rootGroup.getPartitions().get().equals(expPartitions)) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected redundancy per leaf group to be " +
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected redundancy per leaf group to be " +
redundancyPerGroup + ", but it is not according to distribution partitions '" +
rootGroup.getPartitions().get() + "'. Expected distribution partitions should be '" + expPartitions + "'.");
}
@@ -98,20 +98,20 @@ public class IndexedHierarchicDistributionValidator {
return sb.toString();
}
- private void validateThatReadyCopiesIsCompatibleWithRedundancy(int groupCount) throws Exception {
- if (redundancy.effectiveFinalRedundancy() % groupCount != 0) {
- throw new Exception(getErrorMsgPrefix() + "Expected equal redundancy per group.");
+ static public void validateThatReadyCopiesIsCompatibleWithRedundancy(String clusterName, int totalRedundancy, int totalReadyCopies, int groupCount) {
+ if (totalRedundancy % groupCount != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected equal redundancy per group.");
}
- if (redundancy.effectiveReadyCopies() % groupCount != 0) {
- throw new Exception(getErrorMsgPrefix() + "Expected equal amount of ready copies per group, but " +
- redundancy.effectiveReadyCopies() + " ready copies is specified with " + groupCount + " groups");
+ if (totalReadyCopies % groupCount != 0) {
+ throw new IllegalArgumentException(getErrorMsgPrefix(clusterName) + "Expected equal amount of ready copies per group, but " +
+ totalReadyCopies + " ready copies is specified with " + groupCount + " groups");
}
- if (redundancy.effectiveReadyCopies() == 0) {
- System.err.println(getErrorMsgPrefix() + "Warning. No ready copies configured. At least one is recommended.");
+ if (totalReadyCopies == 0) {
+ System.err.println(getErrorMsgPrefix(clusterName) + "Warning. No ready copies configured. At least one is recommended.");
}
}
- private String getErrorMsgPrefix() {
+ static private String getErrorMsgPrefix(String clusterName) {
return "In indexed content cluster '" + clusterName + "' using hierarchic distribution: ";
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java b/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
index 09a6f9b5c53..0ff41019ae8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/Redundancy.java
@@ -11,55 +11,35 @@ import com.yahoo.vespa.config.search.core.ProtonConfig;
*/
public class Redundancy implements StorDistributionConfig.Producer, ProtonConfig.Producer {
+ // This numbers are all per group as wanted numbers.
private final int initialRedundancy ;
private final int finalRedundancy;
private final int readyCopies;
- private int implicitGroups = 1;
- private int explicitGroups = 1;
+ private final int groups;
/** The total number of nodes available in this cluster (assigned when this becomes known) */
- private int totalNodes = 0;
+ private final int totalNodes;
- public Redundancy(int initialRedundancy, int finalRedundancy, int readyCopies) {
+ public Redundancy(int initialRedundancy, int finalRedundancy, int readyCopies, int groups, int totalNodes) {
this.initialRedundancy = initialRedundancy;
this.finalRedundancy = finalRedundancy;
this.readyCopies = readyCopies;
+ this.groups = groups;
+ this.totalNodes = totalNodes;
}
- /**
- * Set the total number of nodes available in this cluster.
- * This impacts the effective redundancy in the case where there are fewer nodes available than
- * the requested redundancy.
- */
- public void setTotalNodes(int totalNodes) { this.totalNodes = totalNodes; }
+ public int finalRedundancy() { return effectiveFinalRedundancy()/groups; }
+ public int readyCopies() { return effectiveReadyCopies()/groups; }
+ public int groups() { return groups; }
- /**
- * Sets the number of groups resulting from implicit setup (groups attribute)
- * in this cluster. With implicit groups the redundancy settings are taken to be
- * <i>per group</i> and are multiplied by this number to get the effective <i>total</i>
- * values returned in the config.
- */
- public void setImplicitGroups(int implicitGroups) { this.implicitGroups = implicitGroups; }
-
- public void setExplicitGroups(int explicitGroups) { this.explicitGroups = explicitGroups; }
-
- public int initialRedundancy() { return initialRedundancy; }
- public int finalRedundancy() { return finalRedundancy; }
- public int readyCopies() { return readyCopies; }
- public int totalNodes() {
- return totalNodes;
- }
-
- public int effectiveInitialRedundancy() { return Math.min(totalNodes, initialRedundancy * implicitGroups); }
- public int effectiveFinalRedundancy() { return Math.min(totalNodes, finalRedundancy * implicitGroups); }
- public int effectiveReadyCopies() { return Math.min(totalNodes, readyCopies * implicitGroups); }
+ public int effectiveInitialRedundancy() { return Math.min(totalNodes, initialRedundancy * groups); }
+ public int effectiveFinalRedundancy() { return Math.min(totalNodes, finalRedundancy * groups); }
+ public int effectiveReadyCopies() { return Math.min(totalNodes, readyCopies * groups); }
public boolean isEffectivelyGloballyDistributed() {
return totalNodes == effectiveFinalRedundancy();
}
- public int redundancyFromSearchNodePerspective() { return finalRedundancy/explicitGroups; }
- public int searchableCopies() { return readyCopies/(explicitGroups*implicitGroups); }
@Override
public void getConfig(StorDistributionConfig.Builder builder) {
@@ -70,8 +50,8 @@ public class Redundancy implements StorDistributionConfig.Producer, ProtonConfig
@Override
public void getConfig(ProtonConfig.Builder builder) {
ProtonConfig.Distribution.Builder distBuilder = new ProtonConfig.Distribution.Builder();
- distBuilder.redundancy(redundancyFromSearchNodePerspective());
- distBuilder.searchablecopies(searchableCopies());
+ distBuilder.redundancy(finalRedundancy());
+ distBuilder.searchablecopies(readyCopies());
builder.distribution(distBuilder);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
index 84132427427..4d1252a2618 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
@@ -15,6 +15,7 @@ import com.yahoo.vespa.model.builder.xml.dom.NodesSpecification;
import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
+import com.yahoo.vespa.model.content.cluster.RedundancyBuilder;
import com.yahoo.vespa.model.content.engines.PersistenceEngine;
import java.util.ArrayList;
@@ -37,7 +38,7 @@ public class StorageGroup {
private final String index;
private Optional<String> partitions;
String name;
- private final ContentCluster owner;
+ private final boolean isHosted;
private final Optional<Long> mmapNoCoreLimit;
private final Optional<Boolean> coreOnOOM;
private final Optional<String> noVespaMalloc;
@@ -51,7 +52,7 @@ public class StorageGroup {
/**
* Creates a storage group
*
- * @param owner the cluster this group belongs to
+ * @param isHosted true if this is in a hosted setup
* @param name the name of this group
* @param index the distribution-key index og this group
* @param partitions the distribution strategy to use to distribute content to subgroups or empty
@@ -59,12 +60,12 @@ public class StorageGroup {
* (having nodes, not subgroups as children).
* @param useCpuSocketAffinity whether processes should be started with socket affinity
*/
- private StorageGroup(ContentCluster owner, String name, String index, Optional<String> partitions,
+ private StorageGroup(boolean isHosted, String name, String index, Optional<String> partitions,
boolean useCpuSocketAffinity, Optional<Long> mmapNoCoreLimit, Optional<Boolean> coreOnOOM,
Optional<String> noVespaMalloc, Optional<String> vespaMalloc,
Optional<String> vespaMallocDebug, Optional<String> vespaMallocDebugStackTrace)
{
- this.owner = owner;
+ this.isHosted = isHosted;
this.index = index;
this.name = name;
this.partitions = partitions;
@@ -76,8 +77,8 @@ public class StorageGroup {
this.vespaMallocDebug = vespaMallocDebug;
this.vespaMallocDebugStackTrace = vespaMallocDebugStackTrace;
}
- private StorageGroup(ContentCluster owner, String name, String index) {
- this(owner, name, index, Optional.empty(), false, Optional.empty(),Optional.empty(), Optional.empty(),
+ private StorageGroup(boolean isHosted, String name, String index) {
+ this(isHosted, name, index, Optional.empty(), false, Optional.empty(),Optional.empty(), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty());
}
@@ -90,7 +91,7 @@ public class StorageGroup {
/** Returns the nodes of this, or an empty list of it is not a leaf group */
public List<StorageNode> getNodes() { return nodes; }
- public ContentCluster getOwner() { return owner; }
+ public boolean isHosted() { return isHosted; }
/** Returns the index of this group, or null if it is the root group */
public String getIndex() { return index; }
@@ -194,16 +195,14 @@ public class StorageGroup {
public static class Builder {
private final ModelElement clusterElement;
- private final ContentCluster owner;
private final ConfigModelContext context;
- public Builder(ModelElement clusterElement, ContentCluster owner, ConfigModelContext context) {
+ public Builder(ModelElement clusterElement, ConfigModelContext context) {
this.clusterElement = clusterElement;
- this.owner = owner;
this.context = context;
}
- public StorageGroup buildRootGroup(DeployState deployState) {
+ public StorageGroup buildRootGroup(DeployState deployState, RedundancyBuilder redundancyBuilder, ContentCluster owner) {
Optional<ModelElement> group = Optional.ofNullable(clusterElement.child("group"));
Optional<ModelElement> nodes = getNodes(clusterElement);
@@ -212,12 +211,28 @@ public class StorageGroup {
if (group.isPresent() && (group.get().stringAttribute("name") != null || group.get().integerAttribute("distribution-key") != null))
deployState.getDeployLogger().log(LogLevel.INFO, "'distribution-key' attribute on a content cluster's root group is ignored");
- GroupBuilder groupBuilder = collectGroup(group, nodes, null, null);
- if (owner.isHostedVespa()) {
- return groupBuilder.buildHosted(deployState, owner, Optional.empty());
- } else {
- return groupBuilder.buildNonHosted(deployState, owner, Optional.empty());
+ GroupBuilder groupBuilder = collectGroup(owner.isHosted(), group, nodes, null, null);
+ StorageGroup storageGroup = (owner.isHosted())
+ ? groupBuilder.buildHosted(deployState, owner, Optional.empty())
+ : groupBuilder.buildNonHosted(deployState, owner, Optional.empty());
+ Redundancy redundancy = redundancyBuilder.build(owner.getName(), owner.isHosted(), storageGroup.subgroups.size(), storageGroup.getNumberOfLeafGroups(), storageGroup.countNodes());
+ owner.setRedundancy(redundancy);
+ if (storageGroup.partitions.isEmpty() && (redundancy.groups() > 1)) {
+ storageGroup.partitions = Optional.of(computePartitions(redundancy.finalRedundancy(), redundancy.groups()));
}
+ return storageGroup;
+ }
+
+ /** This returns a partition string which specifies equal distribution between all groups */
+ // TODO: Make a partitions object
+ static private String computePartitions(int redundancyPerGroup, int numGroups) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < numGroups - 1; ++i) {
+ sb.append(redundancyPerGroup);
+ sb.append("|");
+ }
+ sb.append("*");
+ return sb.toString();
}
/**
@@ -262,9 +277,6 @@ public class StorageGroup {
if ( ! parent.isPresent() && subGroups.isEmpty() && nodeBuilders.isEmpty()) // no nodes or groups: create single node
storageGroup.nodes.add(buildSingleNode(deployState, owner));
-
- if ( ! parent.isPresent())
- owner.redundancy().setTotalNodes(storageGroup.countNodes());
return storageGroup;
}
@@ -297,19 +309,11 @@ public class StorageGroup {
if (hostGroups.size() > 1) {
if (parent.isPresent())
throw new IllegalArgumentException("Cannot specify groups using the groups attribute in nested content groups");
- owner.redundancy().setTotalNodes(hostMapping.size());
-
- // Switch redundancy settings to meaning "per group"
- owner.redundancy().setImplicitGroups(hostGroups.size());
-
- // Compute partitions expression
- int redundancyPerGroup = (int)Math.floor(owner.redundancy().effectiveFinalRedundancy() / hostGroups.size());
- storageGroup.partitions = Optional.of(computePartitions(redundancyPerGroup, hostGroups.size()));
// create subgroups as returned from allocation
for (Map.Entry<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> hostGroup : hostGroups.entrySet()) {
String groupIndex = String.valueOf(hostGroup.getKey().get().index());
- StorageGroup subgroup = new StorageGroup(owner, groupIndex, groupIndex);
+ StorageGroup subgroup = new StorageGroup(true, groupIndex, groupIndex);
for (Map.Entry<HostResource, ClusterMembership> host : hostGroup.getValue().entrySet()) {
subgroup.nodes.add(createStorageNode(deployState, owner, host.getKey(), subgroup, host.getValue()));
}
@@ -323,24 +327,10 @@ public class StorageGroup {
for (GroupBuilder subGroup : subGroups) {
storageGroup.subgroups.add(subGroup.buildHosted(deployState, owner, Optional.of(this)));
}
- if ( ! parent.isPresent())
- owner.redundancy().setTotalNodes(storageGroup.countNodes());
}
return storageGroup;
}
- /** This returns a partition string which specifies equal distribution between all groups */
- // TODO: Make a partitions object
- private String computePartitions(int redundancyPerGroup, int numGroups) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < numGroups - 1; ++i) {
- sb.append(redundancyPerGroup);
- sb.append("|");
- }
- sb.append("*");
- return sb.toString();
- }
-
/** Collect hosts per group */
private Map<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> collectAllocatedSubgroups(Map<HostResource, ClusterMembership> hostMapping) {
Map<Optional<ClusterSpec.Group>, Map<HostResource, ClusterMembership>> hostsPerGroup = new LinkedHashMap<>();
@@ -387,9 +377,9 @@ public class StorageGroup {
* <li>Neither element is present: Create a single node.
* </ul>
*/
- private GroupBuilder collectGroup(Optional<ModelElement> groupElement, Optional<ModelElement> nodesElement, String name, String index) {
+ private GroupBuilder collectGroup(boolean isHosted, Optional<ModelElement> groupElement, Optional<ModelElement> nodesElement, String name, String index) {
StorageGroup group = new StorageGroup(
- owner, name, index,
+ isHosted, name, index,
childAsString(groupElement, "distribution.partitions"),
booleanAttributeOr(groupElement, VespaDomBuilder.CPU_SOCKET_AFFINITY_ATTRIB_NAME, false),
childAsLong(groupElement, VespaDomBuilder.MMAP_NOCORE_LIMIT),
@@ -399,7 +389,7 @@ public class StorageGroup {
childAsString(groupElement, VespaDomBuilder.VESPAMALLOC_DEBUG),
childAsString(groupElement, VespaDomBuilder.VESPAMALLOC_DEBUG_STACKTRACE));
- List<GroupBuilder> subGroups = groupElement.isPresent() ? collectSubGroups(group, groupElement.get()) : Collections.emptyList();
+ List<GroupBuilder> subGroups = groupElement.isPresent() ? collectSubGroups(isHosted, group, groupElement.get()) : Collections.emptyList();
List<XmlNodeBuilder> explicitNodes = new ArrayList<>();
explicitNodes.addAll(collectExplicitNodes(groupElement));
@@ -450,7 +440,7 @@ public class StorageGroup {
return nodes;
}
- private List<GroupBuilder> collectSubGroups(StorageGroup parentGroup, ModelElement parentGroupElement) {
+ private List<GroupBuilder> collectSubGroups(boolean isHosted, StorageGroup parentGroup, ModelElement parentGroupElement) {
List<ModelElement> subGroupElements = parentGroupElement.subElements("group");
if (subGroupElements.size() > 1 && ! parentGroup.getPartitions().isPresent())
throw new IllegalArgumentException("'distribution' attribute is required with multiple subgroups");
@@ -461,7 +451,7 @@ public class StorageGroup {
indexPrefix = parentGroup.index + ".";
}
for (ModelElement g : subGroupElements) {
- subGroups.add(collectGroup(Optional.of(g), Optional.ofNullable(g.child("nodes")), g.stringAttribute("name"),
+ subGroups.add(collectGroup(isHosted, Optional.of(g), Optional.ofNullable(g.child("nodes")), g.stringAttribute("name"),
indexPrefix + g.integerAttribute("distribution-key")));
}
return subGroups;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index 833afb67f58..48779657162 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -80,9 +80,9 @@ public class ContentCluster extends AbstractConfigProducer implements
MessagetyperouteselectorpolicyConfig.Producer,
BucketspacesConfig.Producer {
- private String documentSelection;
+ private final String documentSelection;
private ContentSearchCluster search;
- private final boolean isHostedVespa;
+ private final boolean isHosted;
private final Map<String, NewDocumentType> documentDefinitions;
private final Set<NewDocumentType> globallyDistributedDocuments;
private com.yahoo.vespa.model.content.StorageGroup rootGroup;
@@ -91,9 +91,9 @@ public class ContentCluster extends AbstractConfigProducer implements
private Redundancy redundancy;
private ClusterControllerConfig clusterControllerConfig;
private PersistenceEngine.PersistenceFactory persistenceFactory;
- private String clusterName;
+ private final String clusterName;
private Integer maxNodesPerMerge;
- private Zone zone;
+ private final Zone zone;
/**
* If multitenant or a cluster controller was explicitly configured in this cluster:
@@ -124,21 +124,20 @@ public class ContentCluster extends AbstractConfigProducer implements
new SearchDefinitionBuilder().build(deployState.getDocumentModel().getDocumentManager(), documentsElement);
String routingSelection = new DocumentSelectionBuilder().build(documentsElement);
- Redundancy redundancy = new RedundancyBuilder().build(contentElement);
+ RedundancyBuilder redundancyBuilder = new RedundancyBuilder(contentElement);
Set<NewDocumentType> globallyDistributedDocuments = new GlobalDistributionBuilder(documentDefinitions).build(documentsElement);
ContentCluster c = new ContentCluster(context.getParentProducer(), getClusterName(contentElement), documentDefinitions,
- globallyDistributedDocuments, routingSelection, redundancy,
+ globallyDistributedDocuments, routingSelection,
deployState.zone(), deployState.isHosted());
c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterName(contentElement), contentElement).build(deployState, c, contentElement.getXml());
c.search = new ContentSearchCluster.Builder(documentDefinitions, globallyDistributedDocuments).build(deployState, c, contentElement.getXml());
c.persistenceFactory = new EngineFactoryBuilder().build(contentElement, c);
c.storageNodes = new StorageCluster.Builder().build(deployState, c, w3cContentElement);
c.distributorNodes = new DistributorCluster.Builder(c).build(deployState, c, w3cContentElement);
- c.rootGroup = new StorageGroup.Builder(contentElement, c, context).buildRootGroup(deployState);
+ c.rootGroup = new StorageGroup.Builder(contentElement, context).buildRootGroup(deployState, redundancyBuilder, c);
validateThatGroupSiblingsAreUnique(c.clusterName, c.rootGroup);
- redundancy.setExplicitGroups(c.getRootGroup().getNumberOfLeafGroups());
- c.search.handleRedundancy(redundancy);
+ c.search.handleRedundancy(c.redundancy);
IndexedSearchCluster index = c.search.getIndexed();
if (index != null) {
@@ -492,14 +491,13 @@ public class ContentCluster extends AbstractConfigProducer implements
private ContentCluster(AbstractConfigProducer parent, String clusterName,
Map<String, NewDocumentType> documentDefinitions,
Set<NewDocumentType> globallyDistributedDocuments,
- String routingSelection, Redundancy redundancy, Zone zone, boolean isHostedVespa) {
+ String routingSelection, Zone zone, boolean isHosted) {
super(parent, clusterName);
- this.isHostedVespa = isHostedVespa;
+ this.isHosted = isHosted;
this.clusterName = clusterName;
this.documentDefinitions = documentDefinitions;
this.globallyDistributedDocuments = globallyDistributedDocuments;
this.documentSelection = routingSelection;
- this.redundancy = redundancy;
this.zone = zone;
}
@@ -553,6 +551,10 @@ public class ContentCluster extends AbstractConfigProducer implements
public final ContentSearchCluster getSearch() { return search; }
public Redundancy redundancy() { return redundancy; }
+ public ContentCluster setRedundancy(Redundancy redundancy) {
+ this.redundancy = redundancy;
+ return this;
+ }
@Override
public void getConfig(MessagetyperouteselectorpolicyConfig.Builder builder) {
@@ -639,14 +641,14 @@ public class ContentCluster extends AbstractConfigProducer implements
}
}
- public boolean isHostedVespa() {
- return isHostedVespa;
+ public boolean isHosted() {
+ return isHosted;
}
@Override
public void validate() throws Exception {
super.validate();
- if (search.usesHierarchicDistribution() && ! isHostedVespa) {
+ if (search.usesHierarchicDistribution() && !isHosted) {
// validate manually configured groups
new IndexedHierarchicDistributionValidator(search.getClusterName(), rootGroup, redundancy, search.getIndexed().getTuning().dispatch.policy).validate();
if (search.getIndexed().useMultilevelDispatchSetup()) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
index fe73fcc904b..b2d28eec0ef 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/RedundancyBuilder.java
@@ -2,22 +2,22 @@
package com.yahoo.vespa.model.content.cluster;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
+import com.yahoo.vespa.model.content.IndexedHierarchicDistributionValidator;
import com.yahoo.vespa.model.content.Redundancy;
/**
* Builds redundancy config for a content cluster.
*/
public class RedundancyBuilder {
+ Integer initialRedundancy = 2;
+ Integer finalRedundancy = 2;
+ Integer readyCopies = 2;
- Redundancy build(ModelElement clusterXml) {
- Integer initialRedundancy = 2;
- Integer finalRedundancy = 3;
- Integer readyCopies = 2;
-
+ RedundancyBuilder(ModelElement clusterXml) {
ModelElement redundancyElement = clusterXml.child("redundancy");
if (redundancyElement != null) {
initialRedundancy = redundancyElement.integerAttribute("reply-after");
- finalRedundancy = (int)redundancyElement.asLong();
+ finalRedundancy = (int) redundancyElement.asLong();
if (initialRedundancy == null) {
initialRedundancy = finalRedundancy;
@@ -35,8 +35,16 @@ public class RedundancyBuilder {
throw new IllegalArgumentException("Number of searchable copies can not be higher than final redundancy");
}
}
-
- return new Redundancy(initialRedundancy, finalRedundancy, readyCopies);
+ }
+ public Redundancy build(String clusterName, boolean isHosted, int subGroups, int leafGroups, int totalNodes) {
+ if (isHosted) {
+ return new Redundancy(initialRedundancy, finalRedundancy, readyCopies, leafGroups, totalNodes);
+ } else {
+ subGroups = Math.max(1, subGroups);
+ IndexedHierarchicDistributionValidator.validateThatLeafGroupsCountIsAFactorOfRedundancy(clusterName, finalRedundancy, subGroups);
+ IndexedHierarchicDistributionValidator.validateThatReadyCopiesIsCompatibleWithRedundancy(clusterName, finalRedundancy, readyCopies, subGroups);
+ return new Redundancy(initialRedundancy/leafGroups, finalRedundancy/leafGroups, readyCopies/leafGroups, leafGroups, totalNodes);
+ }
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
index 8ae68468374..f36ef6c3ba3 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
@@ -113,6 +113,7 @@ public class DistributorTest {
assertEquals(true, conf.inlinebucketsplitting());
cluster = parseCluster("<cluster id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
" <documents/>" +
" <tuning>" +
" <distribution type=\"legacy\"/>" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
index 3b2a741a177..eef48120da8 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/RedundancyTest.java
@@ -22,9 +22,7 @@ public class RedundancyTest {
}
private static Redundancy createRedundancy(int redundancy, int implicitGroups, int totalNodes) {
- Redundancy r = new Redundancy(1, redundancy, 1);
- r.setImplicitGroups(implicitGroups);
- r.setTotalNodes(totalNodes);
+ Redundancy r = new Redundancy(1, redundancy, 1, implicitGroups, totalNodes);
return r;
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
index bbb101dbb08..21c384dfc69 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
@@ -305,7 +305,8 @@ public class StorageClusterTest {
public void requireThatGroupNamesMustBeUniqueAmongstSiblings() {
String xml =
"<cluster id=\"storage\">\n" +
- "<documents/>\n" +
+ " <redundancy>2</redundancy>" +
+ " <documents/>\n" +
" <group>\n" +
" <distribution partitions=\"*\"/>\n" +
" <group distribution-key=\"0\" name=\"bar\">\n" +
@@ -330,6 +331,7 @@ public class StorageClusterTest {
public void requireThatGroupNamesCanBeDuplicatedAcrossLevels() {
String xml =
"<cluster id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
"<documents/>\n" +
" <group>\n" +
" <distribution partitions=\"*\"/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
index c1789a05f3c..cb457cabf6c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageGroupTest.java
@@ -80,6 +80,7 @@ public class StorageGroupTest {
StorDistributionConfig.Builder builder = new StorDistributionConfig.Builder();
parse(
"<content version=\"1.0\" id=\"storage\">\n" +
+ " <redundancy>4</redundancy>" +
" <documents/>" +
" <group>\n" +
" <distribution partitions=\"1|*\"/>\n" +
@@ -134,6 +135,7 @@ public class StorageGroupTest {
StorDistributionConfig.Builder builder = new StorDistributionConfig.Builder();
parse(
"<content version=\"1.0\" id=\"storage\">\n" +
+ " <redundancy>2</redundancy>" +
" <documents/>" +
" <group>\n" +
" <distribution partitions=\"1|*\"/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
index e4decbc9a10..4fadea74feb 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/cluster/ClusterTest.java
@@ -124,6 +124,8 @@ public class ClusterTest {
" </engine>",
" <group>",
" <node hostalias='my_host' distribution-key='0' />",
+ " <node hostalias='my_host' distribution-key='1' />",
+ " <node hostalias='my_host' distribution-key='2' />",
" </group>",
contentSearchXml,
" </content>",