use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class ProvisioningTest method out_of_capacity_all_nodes_want_to_retire.
@Test
public void out_of_capacity_all_nodes_want_to_retire() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
ApplicationId application = tester.makeApplicationId();
// Flag all nodes for retirement
List<Node> readyNodes = tester.makeReadyNodes(5, "default");
readyNodes.forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true))));
try {
prepare(application, 2, 0, 2, 0, "default", tester);
fail("Expected exception");
} catch (OutOfCapacityException e) {
assertTrue(e.getMessage().startsWith("Could not satisfy request"));
}
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class ProvisioningTest method application_deployment_extends_existing_reservations_on_deploy.
@Test
public void application_deployment_extends_existing_reservations_on_deploy() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
ApplicationId application = tester.makeApplicationId();
tester.makeReadyNodes(2, "default");
// Deploy fails with out of capacity
try {
prepare(application, 2, 0, 2, 0, "default", tester);
fail("Expected exception");
} catch (OutOfCapacityException ignored) {
}
assertEquals("Reserved a subset of required nodes", 2, tester.getNodes(application, Node.State.reserved).size());
// Enough nodes become available
tester.makeReadyNodes(2, "default");
// Deploy is retried after a few minutes
tester.clock().advance(Duration.ofMinutes(2));
SystemState state = prepare(application, 2, 0, 2, 0, "default", tester);
List<Node> reserved = tester.getNodes(application, Node.State.reserved).asList();
assertEquals("Reserved required nodes", 4, reserved.size());
assertTrue("Time of event is updated for all nodes", reserved.stream().allMatch(n -> n.history().event(History.Event.Type.reserved).get().at().equals(tester.clock().instant())));
// Over 10 minutes pass since first reservation. First set of reserved nodes are not expired
tester.clock().advance(Duration.ofMinutes(8).plus(Duration.ofSeconds(1)));
ReservationExpirer expirer = new ReservationExpirer(tester.nodeRepository(), tester.clock(), Duration.ofMinutes(10), new JobControl(tester.nodeRepository().database()));
expirer.run();
assertEquals("Nodes remain reserved", 4, tester.getNodes(application, Node.State.reserved).size());
tester.activate(application, state.allHosts);
assertEquals(4, tester.getNodes(application, Node.State.active).size());
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class ResourceCapacityTest method basic_capacity_and_compare_operations.
@Test
public void basic_capacity_and_compare_operations() {
FlavorConfigBuilder b = new FlavorConfigBuilder();
b.addFlavor("host-large", 6., 6., 6, Flavor.Type.BARE_METAL);
b.addFlavor("host-small", 3., 3., 3, Flavor.Type.BARE_METAL);
b.addFlavor("d-1", 1, 1., 1, Flavor.Type.DOCKER_CONTAINER);
b.addFlavor("d-2", 2, 2., 2, Flavor.Type.DOCKER_CONTAINER);
b.addFlavor("d-3", 3, 3., 3, Flavor.Type.DOCKER_CONTAINER);
b.addFlavor("d-3-disk", 3, 3., 5, Flavor.Type.DOCKER_CONTAINER);
b.addFlavor("d-3-mem", 3, 5., 3, Flavor.Type.DOCKER_CONTAINER);
b.addFlavor("d-3-cpu", 5, 3., 3, Flavor.Type.DOCKER_CONTAINER);
FlavorsConfig flavors = b.build();
Flavor hostLargeFlavor = new Flavor(flavors.flavor(0));
Flavor hostSmallFlavor = new Flavor(flavors.flavor(1));
Flavor d1Flavor = new Flavor(flavors.flavor(2));
Flavor d2Flavor = new Flavor(flavors.flavor(3));
Flavor d3Flavor = new Flavor(flavors.flavor(4));
Flavor d3DiskFlavor = new Flavor(flavors.flavor(5));
Flavor d3MemFlavor = new Flavor(flavors.flavor(6));
Flavor d3CPUFlavor = new Flavor(flavors.flavor(7));
Node nodeHostSmall = node(hostSmallFlavor);
ResourceCapacity capacityOfHostSmall = new ResourceCapacity(nodeHostSmall);
// Assert initial capacities
assertTrue(capacityOfHostSmall.hasCapacityFor(hostSmallFlavor));
assertTrue(capacityOfHostSmall.hasCapacityFor(d1Flavor));
assertTrue(capacityOfHostSmall.hasCapacityFor(d2Flavor));
assertTrue(capacityOfHostSmall.hasCapacityFor(d3Flavor));
assertFalse(capacityOfHostSmall.hasCapacityFor(hostLargeFlavor));
// Also check that we are taking all three resources into accout
assertFalse(capacityOfHostSmall.hasCapacityFor(d3DiskFlavor));
assertFalse(capacityOfHostSmall.hasCapacityFor(d3MemFlavor));
assertFalse(capacityOfHostSmall.hasCapacityFor(d3CPUFlavor));
// Compare it to various flavors
assertEquals(1, capacityOfHostSmall.compare(nodeCapacity(d1Flavor)));
assertEquals(1, capacityOfHostSmall.compare(nodeCapacity(d2Flavor)));
assertEquals(0, capacityOfHostSmall.compare(nodeCapacity(d3Flavor)));
assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3DiskFlavor)));
assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3CPUFlavor)));
assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3MemFlavor)));
// Change free capacity and assert on rest capacity
capacityOfHostSmall.subtract(node(d1Flavor));
assertEquals(0, capacityOfHostSmall.compare(nodeCapacity(d2Flavor)));
// Assert on rest capacity
assertTrue(capacityOfHostSmall.hasCapacityFor(d1Flavor));
assertFalse(capacityOfHostSmall.hasCapacityFor(d3Flavor));
// At last compare the disk and cpu and mem variations
assertEquals(-1, nodeCapacity(d3Flavor).compare(nodeCapacity(d3DiskFlavor)));
assertEquals(1, nodeCapacity(d3DiskFlavor).compare(nodeCapacity(d3CPUFlavor)));
assertEquals(-1, nodeCapacity(d3CPUFlavor).compare(nodeCapacity(d3MemFlavor)));
assertEquals(1, nodeCapacity(d3MemFlavor).compare(nodeCapacity(d3DiskFlavor)));
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class MultigroupProvisioningTest method deploy.
private void deploy(ApplicationId application, Capacity capacity, int wantedGroups, ProvisioningTester tester) {
int nodeCount = capacity.nodeCount();
String flavor = capacity.flavor().get();
int previousActiveNodeCount = tester.getNodes(application, Node.State.active).flavor(flavor).size();
tester.activate(application, prepare(application, capacity, wantedGroups, tester));
assertEquals("Superfluous nodes are retired, but no others - went from " + previousActiveNodeCount + " to " + nodeCount + " nodes", Math.max(0, previousActiveNodeCount - capacity.nodeCount()), tester.getNodes(application, Node.State.active).retired().flavor(flavor).size());
assertEquals("Other flavors are retired", 0, tester.getNodes(application, Node.State.active).nonretired().notFlavor(capacity.flavor().get()).size());
// Check invariants for all nodes
Set<Integer> allIndexes = new HashSet<>();
for (Node node : tester.getNodes(application, Node.State.active).asList()) {
// Node indexes must be unique
int index = node.allocation().get().membership().index();
assertFalse("Node indexes are unique", allIndexes.contains(index));
allIndexes.add(index);
assertTrue(node.allocation().get().membership().cluster().group().isPresent());
}
// Count unretired nodes and groups of the requested flavor
Set<Integer> indexes = new HashSet<>();
Map<ClusterSpec.Group, Integer> nonretiredGroups = new HashMap<>();
for (Node node : tester.getNodes(application, Node.State.active).nonretired().flavor(flavor).asList()) {
indexes.add(node.allocation().get().membership().index());
ClusterSpec.Group group = node.allocation().get().membership().cluster().group().get();
nonretiredGroups.put(group, nonretiredGroups.getOrDefault(group, 0) + 1);
if (wantedGroups > 1)
assertTrue("Group indexes are always in [0, wantedGroups>", group.index() < wantedGroups);
}
assertEquals("Total nonretired nodes", nodeCount, indexes.size());
assertEquals("Total nonretired groups", wantedGroups, nonretiredGroups.size());
for (Integer groupSize : nonretiredGroups.values()) assertEquals("Group size", (long) nodeCount / wantedGroups, (long) groupSize);
Map<ClusterSpec.Group, Integer> allGroups = new HashMap<>();
for (Node node : tester.getNodes(application, Node.State.active).flavor(flavor).asList()) {
ClusterSpec.Group group = node.allocation().get().membership().cluster().group().get();
allGroups.put(group, nonretiredGroups.getOrDefault(group, 0) + 1);
}
assertEquals("No additional groups are retained containing retired nodes", wantedGroups, allGroups.size());
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class NodeTypeProvisioningTest method retire_multiple_proxy_simultaneously.
@Test
public void retire_multiple_proxy_simultaneously() {
MockDeployer deployer = new MockDeployer(tester.provisioner(), Collections.singletonMap(application, new MockDeployer.ApplicationContext(application, clusterSpec, capacity, 1)));
RetiredExpirer retiredExpirer = new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer, tester.clock(), Duration.ofDays(30), Duration.ofMinutes(10), new JobControl(tester.nodeRepository().database()));
final int numNodesToRetire = 5;
{
// Deploy
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals("Reserved all proxies", 11, hosts.size());
tester.activate(application, new HashSet<>(hosts));
List<Node> nodes = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active);
assertEquals("Activated all proxies", 11, nodes.size());
}
List<Node> nodesToRetire = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active).subList(3, 3 + numNodesToRetire);
String currentyRetiringHostname;
{
nodesToRetire.forEach(nodeToRetire -> tester.nodeRepository().write(nodeToRetire.with(nodeToRetire.status().withWantToRetire(true))));
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals(11, hosts.size());
tester.activate(application, new HashSet<>(hosts));
List<Node> nodes = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active);
assertEquals(11, nodes.size());
// Verify that wantToRetire has been propagated
List<Node> nodesCurrentlyRetiring = nodes.stream().filter(node -> node.allocation().get().membership().retired()).collect(Collectors.toList());
assertEquals(1, nodesCurrentlyRetiring.size());
// The retiring node should be one of the nodes we marked for retirement
currentyRetiringHostname = nodesCurrentlyRetiring.get(0).hostname();
assertTrue(nodesToRetire.stream().map(Node::hostname).filter(hostname -> hostname.equals(currentyRetiringHostname)).count() == 1);
}
{
// Redeploying while the node is still retiring has no effect
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals(11, hosts.size());
tester.activate(application, new HashSet<>(hosts));
List<Node> nodes = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active);
assertEquals(11, nodes.size());
// Verify that wantToRetire has been propagated
List<Node> nodesCurrentlyRetiring = nodes.stream().filter(node -> node.allocation().get().membership().retired()).collect(Collectors.toList());
assertEquals(1, nodesCurrentlyRetiring.size());
// The node that started retiring is still the only one retiring
assertEquals(currentyRetiringHostname, nodesCurrentlyRetiring.get(0).hostname());
}
{
tester.advanceTime(Duration.ofMinutes(11));
retiredExpirer.run();
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals(10, hosts.size());
tester.activate(application, new HashSet<>(hosts));
List<Node> nodes = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active);
assertEquals(10, nodes.size());
// Verify the node we previously set to retire has finished retiring
assertEquals(Node.State.inactive, tester.nodeRepository().getNode(currentyRetiringHostname).orElseThrow(RuntimeException::new).state());
// Verify that a node is currently retiring
List<Node> nodesCurrentlyRetiring = nodes.stream().filter(node -> node.allocation().get().membership().retired()).collect(Collectors.toList());
assertEquals(1, nodesCurrentlyRetiring.size());
// This node is different from the one that was retiring previously
String newRetiringHostname = nodesCurrentlyRetiring.get(0).hostname();
assertNotEquals(currentyRetiringHostname, newRetiringHostname);
// ... but is one of the nodes that were put to wantToRetire earlier
assertTrue(nodesToRetire.stream().map(Node::hostname).filter(hostname -> hostname.equals(newRetiringHostname)).count() == 1);
}
for (int i = 0; i < 10; i++) {
tester.advanceTime(Duration.ofMinutes(11));
retiredExpirer.run();
List<HostSpec> hosts = deployProxies(application, tester);
tester.activate(application, new HashSet<>(hosts));
}
// After a long time, all currently active proxy nodes are not marked with wantToRetire or as retired
long numRetiredActiveProxyNodes = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active).stream().filter(node -> !node.status().wantToRetire()).filter(node -> !node.allocation().get().membership().retired()).count();
assertEquals(11 - numNodesToRetire, numRetiredActiveProxyNodes);
// All the nodes that were marked with wantToRetire earlier are now inactive
assertEquals(nodesToRetire.stream().map(Node::hostname).collect(Collectors.toSet()), tester.nodeRepository().getNodes(Node.State.inactive).stream().map(Node::hostname).collect(Collectors.toSet()));
}
Aggregations