@Override
    protected double maintain() {
        if ( ! nodeRepository().nodes().isWorking()) return 0.0;
        if (nodeRepository().zone().environment().isTest()) return 1.0;

        int attempts = 0;
        int failures = 0;
        outer:
        for (var applicationNodes : activeNodesByApplication().entrySet()) {
            for (var clusterNodes : nodesByCluster(applicationNodes.getValue()).entrySet()) {
                if (shuttingDown()) break outer;
                attempts++;
                if ( ! autoscale(applicationNodes.getKey(), clusterNodes.getKey()))
                    failures++;
            }
        }
        return asSuccessFactorDeviation(attempts, failures);
    }
@Test
    public void test_scaling_event_recording() {
        ApplicationId app1 = AutoscalingMaintainerTester.makeApplicationId("app1");
        ClusterSpec cluster1 = AutoscalingMaintainerTester.containerClusterSpec();
        NodeResources lowResources = new NodeResources(4, 4, 10, 0.1);
        NodeResources highResources = new NodeResources(8, 8, 20, 0.1);
        Capacity app1Capacity = Capacity.from(new ClusterResources(2, 1, lowResources),
                                              new ClusterResources(4, 2, highResources));
        var tester = new AutoscalingMaintainerTester(new MockDeployer.ApplicationContext(app1, cluster1, app1Capacity));

        // deploy
        tester.deploy(app1, cluster1, app1Capacity);

        int measurements = 6;
        Duration samplePeriod = Duration.ofSeconds(150);
        for (int i = 0; i < 20; i++) {
            // Record completion to keep scaling window at minimum
            tester.addMeasurements(0.1f, 0.1f, 0.1f, i, 1, app1, cluster1.id());
            tester.maintainer().maintain();

            tester.clock().advance(Duration.ofDays(1));

            if (i % 2 == 0) { // high load
                tester.addMeasurements(0.99f, 0.99f, 0.99f, i, measurements, app1, cluster1.id());
            }
            else { // low load
                tester.addMeasurements(0.2f, 0.2f, 0.2f, i, measurements, app1, cluster1.id());
            }
            tester.clock().advance(samplePeriod.negated().multipliedBy(measurements));
            tester.addQueryRateMeasurements(app1, cluster1.id(), measurements, t -> (t == 0 ? 20.0 : 10.0 ));
            tester.maintainer().maintain();
        }

        assertEquals(Cluster.maxScalingEvents, tester.cluster(app1, cluster1).scalingEvents().size());

        // Complete last event
        tester.addMeasurements(0.1f, 0.1f, 0.1f, 20, 1, app1, cluster1.id());
        tester.maintainer().maintain();
        assertEquals("Last event is completed",
                     tester.clock().instant(),
                     tester.cluster(app1, cluster1).scalingEvents().get(Cluster.maxScalingEvents - 1).completion().get());
    }