private boolean recomputeClusterStateIfRequired() {
        boolean stateWasChanged = false;
        if (mustRecomputeCandidateClusterState()) {
            stateChangeHandler.unsetStateChangedFlag();
            final AnnotatedClusterState candidate = computeCurrentAnnotatedState();
            // TODO test multiple bucket spaces configured
            // TODO what interaction do we want between generated and derived states wrt. auto group take-downs?
            final ClusterStateBundle candidateBundle = ClusterStateBundle.builder(candidate)
                    .bucketSpaces(configuredBucketSpaces)
                    .stateDeriver(createBucketSpaceStateDeriver())
                    .distributionConfig(distributionConfigIfEnabledOrNull())
                    .deferredActivation(options.enableTwoPhaseClusterStateActivation())
                    .feedBlock(createResourceExhaustionCalculator()
                            .inferContentClusterFeedBlockOrNull(cluster))
                    .deriveAndBuild();
            stateVersionTracker.updateLatestCandidateStateBundle(candidateBundle);
            invokeCandidateStateListeners(candidateBundle);

            final long timeNowMs = timer.getCurrentTimeInMillis();
            if (hasPassedFirstStateBroadcastTimePoint(timeNowMs)
                && (stateVersionTracker.candidateChangedEnoughFromCurrentToWarrantPublish()
                    || stateVersionTracker.hasReceivedNewVersionFromZooKeeper()))
            {
                final ClusterStateBundle before = stateVersionTracker.getVersionedClusterStateBundle();

                stateVersionTracker.promoteCandidateToVersionedState(timeNowMs);
                emitEventsForAlteredStateEdges(before, stateVersionTracker.getVersionedClusterStateBundle(), timeNowMs);
                handleNewPublishedState(stateVersionTracker.getVersionedClusterStateBundle());
                stateWasChanged = true;
            }
        }
        /*
         * This works transparently for tasks that end up changing the current cluster state (i.e.
         * requiring a new state to be published) and for those whose changes are no-ops (because
         * the changes they request are already part of the current state). In the former case the
         * tasks will depend on the version that was generated based upon them. In the latter case
         * the tasks will depend on the version that is already published (or in the process of
         * being published).
         */
        scheduleVersionDependentTasksForFutureCompletion(stateVersionTracker.getCurrentVersion());
        return stateWasChanged;
    }
A "consolidated" cluster state is guaranteed to have up-to-date information on which nodes are
up or down even when the whole cluster is down. The regular, published cluster state is not
normally updated to reflect node events when the cluster is down.
clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java