Rebalancer extends NodeRepositoryMaintainer { @Override protected boolean maintain() { boolean success = true; if ( ! nodeRepository().zone().getCloud().allowHostSharing()) return success; if (nodeRepository().zone().environment().isTest()) return success; NodeList allNodes = nodeRepository().list(); updateSkewMetric(allNodes); if ( ! zoneIsStable(allNodes)) return success; findBestMove(allNodes).execute(true, Agent.Rebalancer, deployer, metric, nodeRepository()); return success; } Rebalancer(Deployer deployer,
                      NodeRepository nodeRepository,
                      Metric metric,
                      Clock clock,
                      Duration interval);   }
@Test public void testRebalancing() { RebalancerTester tester = new RebalancerTester(); tester.deployApp(cpuApp); Node cpuSkewedNode = tester.getNode(cpuApp); tester.maintain(); assertFalse("No better place to move the skewed node, so no action is taken", tester.getNode(cpuSkewedNode.hostname()).get().status().wantToRetire()); assertEquals(0.00325, tester.metric().values.get("hostedVespa.docker.skew").doubleValue(), 0.00001); Node newCpuHost = tester.makeReadyNode("cpu"); tester.activateTenantHosts(); tester.maintain(); assertTrue("Rebalancer retired the node we wanted to move away from", tester.isNodeRetired(cpuSkewedNode)); assertTrue("... and added a node on the new host instead", tester.getNodes(cpuApp, Node.State.active).stream().anyMatch(node -> node.hasParent(newCpuHost.hostname()))); assertEquals("Skew is reduced", 0.00244, tester.metric().values.get("hostedVespa.docker.skew").doubleValue(), 0.00001); tester.deployApp(memoryApp); assertEquals("Assigned to a flat node as that causes least skew", "flat", tester.nodeRepository().list().parentOf(tester.getNode(memoryApp)).get().flavor().name()); tester.maintain(); assertEquals("Deploying the mem skewed app increased skew", 0.00734, tester.metric().values.get("hostedVespa.docker.skew").doubleValue(), 0.00001); Node memSkewedNode = tester.getNode(memoryApp); Node newMemHost = tester.makeReadyNode("mem"); tester.activateTenantHosts(); tester.maintain(); assertFalse("No rebalancing happens because cpuSkewedNode is still retired", tester.isNodeRetired(memSkewedNode)); NestedTransaction tx = new NestedTransaction(); tester.nodeRepository().deactivate(List.of(cpuSkewedNode), tx); tx.commit(); tester.deployer().setFailActivate(true); tester.maintain(); assertTrue("Want to retire is reset", tester.getNodes(Node.State.active).stream().noneMatch(node -> node.status().wantToRetire())); assertEquals("Reserved node was moved to dirty", 1, tester.getNodes(Node.State.dirty).size()); String reservedHostname = tester.getNodes(Node.State.dirty).get(0).hostname(); tester.nodeRepository().setReady(reservedHostname, Agent.system, "Cleanup"); tester.nodeRepository().removeRecursively(reservedHostname); tester.deployer().setFailActivate(false); tester.maintain(); assertTrue("Rebalancer retired the node we wanted to move away from", tester.isNodeRetired(memSkewedNode)); assertTrue("... and added a node on the new host instead", tester.getNodes(memoryApp, Node.State.active).stream().anyMatch(node -> node.hasParent(newMemHost.hostname()))); assertEquals("Skew is reduced", 0.00587, tester.metric().values.get("hostedVespa.docker.skew").doubleValue(), 0.00001); }
@Test public void testNoRebalancingIfRecentlyDeployed() { RebalancerTester tester = new RebalancerTester(); tester.deployApp(cpuApp); Node cpuSkewedNode = tester.getNode(cpuApp); tester.maintain(); assertFalse("No better place to move the skewed node, so no action is taken", tester.getNode(cpuSkewedNode.hostname()).get().status().wantToRetire()); Node newCpuHost = tester.makeReadyNode("cpu"); tester.activateTenantHosts(); tester.deployApp(cpuApp, false ); tester.maintain(); assertFalse("No action, since app was recently deployed", tester.isNodeRetired(cpuSkewedNode)); tester.clock().advance(waitTimeAfterPreviousDeployment); tester.maintain(); assertTrue("Rebalancer retired the node we wanted to move away from", tester.isNodeRetired(cpuSkewedNode)); assertTrue("... and added a node on the new host instead", tester.getNodes(cpuApp, Node.State.active).stream().anyMatch(node -> node.hasParent(newCpuHost.hostname()))); }