@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 respects_exclusive_allocation() {
        tester = new DynamicProvisioningTester(Cloud.builder().name(CloudName.AWS).dynamicProvisioning(true).allowHostSharing(false).build(), new MockNameResolver());
        NodeResources resources1 = new NodeResources(24, 64, 100, 10);
        setPreprovisionCapacityFlag(tester,
                                    new ClusterCapacity(1, resources1.vcpu(), resources1.memoryGiB(), resources1.diskGb(),
                                                        resources1.bandwidthGbps(), resources1.diskSpeed().name(),
                                                        resources1.storageType().name(), resources1.architecture().name(),
                                                        "container"),
                                    new ClusterCapacity(1, resources1.vcpu(), resources1.memoryGiB(), resources1.diskGb(),
                                                        resources1.bandwidthGbps(), resources1.diskSpeed().name(),
                                                        resources1.storageType().name(), resources1.architecture().name(),
                                                        null));
        tester.maintain();

        // Hosts are provisioned
        assertEquals(2, tester.provisionedHostsMatching(resources1));
        assertEquals(0, tester.hostProvisioner.deprovisionedHosts());
        assertEquals(Optional.empty(), tester.nodeRepository.nodes().node("host100").flatMap(Node::exclusiveToApplicationId));
        assertEquals(Optional.empty(), tester.nodeRepository.nodes().node("host101").flatMap(Node::exclusiveToApplicationId));

        // Next maintenance run does nothing
        tester.assertNodesUnchanged();

        // One host is allocated exclusively to some other application
        tester.nodeRepository.nodes().write(tester.nodeRepository.nodes().node("host100").get()
                                                                 .withExclusiveToApplicationId(ApplicationId.from("t", "a", "i")),
                                            () -> { });

        tester.maintain();

        // New hosts are provisioned, and the empty exclusive host is deallocated
        assertEquals(2, tester.provisionedHostsMatching(resources1));
        assertEquals(1, tester.hostProvisioner.deprovisionedHosts());

        // Next maintenance run does nothing
        tester.assertNodesUnchanged();
    }