aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/vespa/model/content/IndexedHierarchicDistributionValidator.java
blob: 97e90d528637d8f982d334d4f54dba340c450db0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 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.");
        }
    }

}