use of com.yahoo.config.provision.OutOfCapacityException 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.config.provision.OutOfCapacityException in project vespa by vespa-engine.
the class ProvisioningTest method application_deployment_multiple_flavors_with_replacement.
@Test
public void application_deployment_multiple_flavors_with_replacement() {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
ApplicationId application1 = tester.makeApplicationId();
tester.makeReadyNodes(8, "large");
tester.makeReadyNodes(8, "large-variant");
// deploy with flavor which will be fulfilled by some old and new nodes
SystemState state1 = prepare(application1, 2, 2, 4, 4, "old-large1", tester);
tester.activate(application1, state1.allHosts);
// redeploy with increased sizes, this will map to the remaining old/new nodes
SystemState state2 = prepare(application1, 3, 4, 4, 5, "old-large2", tester);
assertEquals("New nodes are reserved", 4, tester.getNodes(application1, Node.State.reserved).size());
tester.activate(application1, state2.allHosts);
assertEquals("All nodes are used", 16, tester.getNodes(application1, Node.State.active).size());
assertEquals("No nodes are retired", 0, tester.getNodes(application1, Node.State.active).retired().size());
// This is a noop as we are already using large nodes and nodes which replace large
SystemState state3 = prepare(application1, 3, 4, 4, 5, "large", tester);
assertEquals("Noop", 0, tester.getNodes(application1, Node.State.reserved).size());
tester.activate(application1, state3.allHosts);
try {
SystemState state4 = prepare(application1, 3, 4, 4, 5, "large-variant", tester);
fail("Should fail as we don't have that many large-variant nodes");
} catch (OutOfCapacityException expected) {
}
// make enough nodes to complete the switch to large-variant
tester.makeReadyNodes(8, "large-variant");
SystemState state4 = prepare(application1, 3, 4, 4, 5, "large-variant", tester);
assertEquals("New 'large-variant' nodes are reserved", 8, tester.getNodes(application1, Node.State.reserved).size());
tester.activate(application1, state4.allHosts);
// (we can not check for the precise state here without carrying over from earlier as the distribution of
// old and new on different clusters is unknown)
}
use of com.yahoo.config.provision.OutOfCapacityException in project vespa by vespa-engine.
the class VirtualNodeProvisioningTest method fail_when_all_hosts_become_clashing.
@Test
public void fail_when_all_hosts_become_clashing() {
tester.makeReadyVirtualNodes(1, flavor, "parentHost1");
tester.makeReadyVirtualNodes(1, flavor, "parentHost2");
tester.makeReadyVirtualNodes(1, flavor, "parentHost3");
tester.makeReadyVirtualNodes(1, flavor, "parentHost4");
int containerNodeCount = 2;
int contentNodeCount = 2;
int groups = 1;
List<HostSpec> containerHosts = prepare(containerClusterSpec, containerNodeCount, groups);
List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups);
activate(containerHosts, contentHosts);
List<Node> nodes = getNodes(applicationId);
assertEquals(4, nodes.size());
assertDistinctParentHosts(nodes, ClusterSpec.Type.container, containerNodeCount);
assertDistinctParentHosts(nodes, ClusterSpec.Type.content, contentNodeCount);
for (Node n : nodes) {
tester.patchNode(n.withParentHostname("clashing"));
}
OutOfCapacityException expected = null;
try {
containerHosts = prepare(containerClusterSpec, containerNodeCount, groups);
} catch (OutOfCapacityException e) {
expected = e;
}
assertNotNull(expected);
}
use of com.yahoo.config.provision.OutOfCapacityException in project vespa by vespa-engine.
the class GroupPreparer method prepare.
/**
* Ensure sufficient nodes are reserved or active for the given application, group and cluster
*
* @param application the application we are allocating to
* @param cluster the cluster and group we are allocating to
* @param requestedNodes a specification of the requested nodes
* @param surplusActiveNodes currently active nodes which are available to be assigned to this group.
* This method will remove from this list if it finds it needs additional nodes
* @param highestIndex the current highest node index among all active nodes in this cluster.
* This method will increase this number when it allocates new nodes to the cluster.
* @param spareCount The number of spare docker hosts we want when dynamically allocate docker containers
* @return the list of nodes this cluster group will have allocated if activated
*/
// Note: This operation may make persisted changes to the set of reserved and inactive nodes,
// but it may not change the set of active nodes, as the active nodes must stay in sync with the
// active config model which is changed on activate
public List<Node> prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, List<Node> surplusActiveNodes, MutableInteger highestIndex, int spareCount) {
try (Mutex lock = nodeRepository.lock(application)) {
// Lock ready pool to ensure that ready nodes are not simultaneously grabbed by others
try (Mutex readyLock = nodeRepository.lockUnallocated()) {
// Create a prioritized set of nodes
NodePrioritizer prioritizer = new NodePrioritizer(nodeRepository.getNodes(), application, cluster, requestedNodes, nodeRepository.getAvailableFlavors(), spareCount, nodeRepository.nameResolver());
prioritizer.addApplicationNodes();
prioritizer.addSurplusNodes(surplusActiveNodes);
prioritizer.addReadyNodes();
prioritizer.addNewDockerNodes();
// Allocate from the prioritized list
NodeAllocation allocation = new NodeAllocation(application, cluster, requestedNodes, highestIndex, nodeRepository);
allocation.offer(prioritizer.prioritize());
if (!allocation.fullfilled())
throw new OutOfCapacityException("Could not satisfy " + requestedNodes + " for " + cluster + outOfCapacityDetails(allocation));
// Extend reservation for already reserved nodes
nodeRepository.reserve(nodeRepository.getNodes(application, Node.State.reserved));
// Carry out and return allocation
nodeRepository.reserve(allocation.reservableNodes());
nodeRepository.addDockerNodes(allocation.newNodes());
surplusActiveNodes.removeAll(allocation.surplusNodes());
return allocation.finalNodes(surplusActiveNodes);
}
}
}
Aggregations