// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
import com.yahoo.vespa.config.content.StorDistributionConfig;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
import com.yahoo.vespa.model.search.SearchNode;
import com.yahoo.yolean.Exceptions;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static com.yahoo.config.model.test.TestUtil.joinLines;
import static com.yahoo.vespa.model.content.utils.ContentClusterUtils.createCluster;
import static com.yahoo.vespa.model.content.utils.ContentClusterUtils.createClusterXml;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for hierarchic distribution in an indexed content cluster.
*
* @author geirst
*/
public class IndexedHierarchicDistributionTest {
private ContentCluster getOneGroupCluster() throws Exception {
String groupXml = joinLines(" ",
" ",
" ",
" ",
" ", "");
return createCluster(createClusterXml(groupXml, 2, 2));
}
private String getTwoGroupsXml(String partitions) {
return joinLines(" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ", "");
}
private ContentCluster getTwoGroupsCluster() throws Exception {
return createCluster(createClusterXml(getTwoGroupsXml("3|*"), 6, 6));
}
private ContentCluster getTwoGroupsCluster(int redundancy, int searchableCopies, String partitions) throws Exception {
return createCluster(createClusterXml(getTwoGroupsXml(partitions), redundancy, searchableCopies));
}
private void assertSearchNode(int expRowId, int expPartitionId, int expDistibutionKey, SearchNode node) {
assertEquals(expRowId, node.getNodeSpec().groupIndex());
assertEquals(expPartitionId, node.getNodeSpec().partitionId());
assertEquals(expDistibutionKey, ((ContentNode)node.getServiceLayerService()).getDistributionKey());
}
private StorDistributionConfig getStorDistributionConfig(ContentCluster c) {
StorDistributionConfig.Builder b = new StorDistributionConfig.Builder();
c.getConfig(b);
return new StorDistributionConfig(b);
}
@Test
void requireThatSearchNodesAreCorrectWithOneGroup() throws Exception {
ContentCluster c = getOneGroupCluster();
List searchNodes = c.getSearch().getSearchNodes();
assertEquals(3, searchNodes.size());
assertSearchNode(0, 0, 0, searchNodes.get(0));
assertSearchNode(0, 1, 1, searchNodes.get(1));
assertSearchNode(0, 2, 2, searchNodes.get(2));
}
@Test
void requireThatActivePerLeafGroupIsDefaultWithOneGroup() throws Exception {
ContentCluster c = getOneGroupCluster();
assertFalse(getStorDistributionConfig(c).active_per_leaf_group());
}
@Test
void requireThatSearchNodesAreCorrectWithTwoGroups() throws Exception {
ContentCluster c = getTwoGroupsCluster();
List searchNodes = c.getSearch().getSearchNodes();
assertEquals(6, searchNodes.size());
assertSearchNode(0, 0, 0, searchNodes.get(0));
assertSearchNode(0, 1, 1, searchNodes.get(1));
assertSearchNode(0, 2, 2, searchNodes.get(2));
assertSearchNode(1, 0, 3, searchNodes.get(3));
assertSearchNode(1, 1, 4, searchNodes.get(4));
assertSearchNode(1, 2, 5, searchNodes.get(5));
}
@Test
void requireThatActivePerLeafGroupIsSetWithTwoGroups() throws Exception {
ContentCluster c = getTwoGroupsCluster();
assertTrue(getStorDistributionConfig(c).active_per_leaf_group());
}
private ContentCluster getIllegalMultipleGroupsLevelCluster() throws Exception {
String groupXml = joinLines(" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ", "");
return createCluster(createClusterXml(groupXml, 2, 2));
}
private String getOddGroupsClusterXml() {
return joinLines(" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ", "");
}
private ContentCluster getIllegalGroupsCluster() throws Exception {
return createCluster(createClusterXml(getOddGroupsClusterXml(), Optional.of(getRoundRobinDispatchXml()), 4, 4));
}
private String getRoundRobinDispatchXml() {
return joinLines("",
" ",
" round-robin",
" ",
"");
}
@Test
void requireThatWeMustHaveOnlyOneGroupLevel() {
try {
getIllegalMultipleGroupsLevelCluster();
fail("Did not get expected Exception");
} catch (Exception e) {
assertEquals("Expected all groups under root group 'null' to be leaf groups only containing nodes, but sub group 'group0' contains 2 sub groups",
Exceptions.toMessageString(e));
}
}
@Test
void requireThatLeafGroupsMustHaveEqualNumberOfNodes() {
try {
getIllegalGroupsCluster();
fail("Did not get expected Exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("leaf group 'group0' contains 1 node(s) while leaf group 'group1' contains 2 node(s)"));
}
}
@Test
void requireThatLeafGroupsCountMustBeAFactorOfRedundancy() {
try {
getTwoGroupsCluster(3, 3, "2|*");
fail("Did not get expected Exception");
} catch (Exception e) {
assertEquals("In content cluster 'mycluster': Expected number of leaf groups (2) to be a factor of redundancy (3), but it is not",
Exceptions.toMessageString(e));
}
}
@Test
void requireThatRedundancyPerGroupMustBeIsEqual() {
try {
getTwoGroupsCluster(4, 4, "1|*");
fail("Did not get expected Exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("Expected distribution partitions should be '2|*'"));
}
}
@Test
void requireThatReadyCopiesMustBeEqualToRedundancy() {
try {
getTwoGroupsCluster(4, 3, "2|*");
fail("Did not get expected Exception");
} catch (Exception e) {
assertEquals("In content cluster 'mycluster': Expected equal amount of ready copies per group, but 3 ready copies is specified with 2 groups", Exceptions.toMessageString(e));
}
}
@Test
void allowLessReadyCopiesThanRedundancy() throws Exception {
getTwoGroupsCluster(4, 2, "2|*");
}
}