com.yahoo.vespa.model.content.IndexedHierarchicDistributionValidator Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
/**
* Class used to validate that hierarchic distribution is correctly setup when having an indexed content cluster.
*
* Note that this class does not implement the com.yahoo.vespa.model.application.validation.Validator interface,
* but is instead used in the context of com.yahoo.vespa.model.ConfigProducer.validate() such that it can be unit tested
* without having to build the complete vespa model.
*
* @author geirst
*/
public class IndexedHierarchicDistributionValidator {
private final StorageGroup rootGroup;
private final Redundancy redundancy;
private final DispatchTuning.DispatchPolicy dispatchPolicy;
public IndexedHierarchicDistributionValidator(StorageGroup rootGroup,
Redundancy redundancy,
DispatchTuning.DispatchPolicy dispatchPolicy) {
this.rootGroup = rootGroup;
this.redundancy = redundancy;
this.dispatchPolicy = dispatchPolicy;
}
public void validate() {
validateThatWeHaveOneGroupLevel();
validateThatLeafGroupsHasEqualNumberOfNodes();
validateThatLeafGroupsCountIsAFactorOfRedundancy(redundancy.effectiveFinalRedundancy(), rootGroup.getSubgroups().size());
validateThatRedundancyPerGroupIsEqual();
validateThatReadyCopiesIsCompatibleWithRedundancy(redundancy.effectiveFinalRedundancy(), redundancy.effectiveReadyCopies(), rootGroup.getSubgroups().size());
}
private void validateThatWeHaveOneGroupLevel() {
for (StorageGroup group : rootGroup.getSubgroups()) {
if (group.getSubgroups().size() > 0) {
throw new IllegalArgumentException("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");
}
}
}
private void validateThatLeafGroupsHasEqualNumberOfNodes() {
if (dispatchPolicy != DispatchTuning.DispatchPolicy.ROUNDROBIN) return;
StorageGroup previousGroup = null;
for (StorageGroup group : rootGroup.getSubgroups()) {
if (previousGroup == null) { // first group
previousGroup = group;
continue;
}
if (group.getNodes().size() != previousGroup.getNodes().size())
throw new IllegalArgumentException("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;
}
}
static public void validateThatLeafGroupsCountIsAFactorOfRedundancy(int totalRedundancy, int subGroups) {
if (totalRedundancy % subGroups != 0) {
throw new IllegalArgumentException("Expected number of leaf groups (" +
subGroups + ") to be a factor of redundancy (" +
totalRedundancy + "), but it is not");
}
}
private void validateThatRedundancyPerGroupIsEqual() {
int redundancyPerGroup = redundancy.effectiveFinalRedundancy() / rootGroup.getSubgroups().size();
String expPartitions = createDistributionPartitions(redundancyPerGroup, rootGroup.getSubgroups().size());
if (!rootGroup.getPartitions().get().equals(expPartitions)) {
throw new IllegalArgumentException("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 + "'");
}
}
private String createDistributionPartitions(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();
}
static public void validateThatReadyCopiesIsCompatibleWithRedundancy(int totalRedundancy, int totalReadyCopies, int groupCount) {
if (totalRedundancy % groupCount != 0) {
throw new IllegalArgumentException("Expected equal redundancy per group");
}
if (totalReadyCopies % groupCount != 0) {
throw new IllegalArgumentException("Expected equal amount of ready copies per group, but " +
totalReadyCopies + " ready copies is specified with " + groupCount + " groups");
}
if (totalReadyCopies == 0) {
throw new IllegalArgumentException("No ready copies configured. At least 1 is required.");
}
}
}