@Override
    protected double maintain() {
        List<Node> provisionedSnapshot;
        try {
            NodeList nodes;
            // Host and child nodes are written in separate transactions, but both are written while holding the
            // unallocated lock. Hold the unallocated lock while reading nodes to ensure we get all the children
            // of newly provisioned hosts.
            try (Mutex ignored = nodeRepository().nodes().lockUnallocated()) {
                nodes = nodeRepository().nodes().list();
            }
            provisionedSnapshot = provision(nodes);
        } catch (NodeAllocationException | IllegalStateException e) {
            log.log(Level.WARNING, "Failed to allocate preprovisioned capacity and/or find excess hosts: " + e.getMessage());
            return 0;  // avoid removing excess hosts
        } catch (RuntimeException e) {
            log.log(Level.WARNING, "Failed to allocate preprovisioned capacity and/or find excess hosts", e);
            return 0;  // avoid removing excess hosts
        }

        return markForRemoval(provisionedSnapshot);
    }
@Test
    public void provision_deficit_and_deprovision_excess() {
        tester = new DynamicProvisioningTester().addInitialNodes();
        setPreprovisionCapacityFlag(tester,
                                    new ClusterCapacity(2, 48.0, 128.0, 1000.0, 10.0, "fast", "remote", "x86_64", null),
                                    new ClusterCapacity(1, 16.0, 24.0, 100.0, 1.0, "fast", "remote", "x86_64", null));

        assertEquals(0, tester.hostProvisioner.provisionedHosts().size());
        assertEquals(9, tester.nodeRepository.nodes().list().size());
        assertNodeExists("host2");
        assertNodeExists("host2-1");
        assertNodeExists("host3");
        assertNodeDoesNotExist("host100");
        assertNodeDoesNotExist("host101");

        tester.maintain();

        assertEquals(2, tester.hostProvisioner.provisionedHosts().size());
        assertEquals(2, tester.provisionedHostsMatching(new NodeResources(48, 128, 1000, 10)));
        NodeList nodesAfter = tester.nodeRepository.nodes().list().not().state(State.deprovisioned);
        assertEquals(9, nodesAfter.size());  // 2 removed, 2 added
        assertSame("Failed host 'host2' is deprovisioned", State.deprovisioned, node("host2").get().state());
        assertNodeDoesNotExist("Node on deprovisioned host removed", "host2-1");
        assertNodeExists("Host satisfying 16-24-100-1 is kept", "host3");
        assertNodeExists("New 48-128-1000-10 host added", "host100");
        assertNodeExists("New 48-128-1000-10 host added", "host100");

        Instant deprovisionedAt = node("host2").get().history().event(History.Event.Type.deprovisioned).get().at();
        tester.provisioningTester.clock().advance(Duration.ofSeconds(1));
        tester.maintain();
        assertEquals("Host moves to deprovisioned once", deprovisionedAt,
                     node("host2").get().history()
                                          .event(History.Event.Type.deprovisioned).get().at());

    }