private void doUpdate(final int taskDelay) {
        securityContext.asProcessingUser(() -> {
            pendingUpdate.set(false);
            final Runnable runnable = taskContextFactory.context(
                    "Create Cluster State",
                    taskContext -> exec(clusterState, taskDelay, true));
            CompletableFuture
                    .runAsync(runnable, executor)
                    .whenComplete((r, t) -> {
                        try {
                            if (t == null) {
                                // Output some debug about the state we are about to set.
                                outputDebug(clusterState);

                                // Re-query cluster state if we are pending an update.
                                if (pendingUpdate.get()) {
                                    // Wait a few seconds before we query again.
                                    Thread.sleep(REQUERY_DELAY);
                                    // Query again.
                                    doUpdate(0);
                                }
                            }
                        } catch (final InterruptedException e) {
                            LOGGER.error(e.getMessage(), e);

                            // Continue to interrupt this thread.
                            Thread.currentThread().interrupt();
                        } catch (final RuntimeException e) {
                            LOGGER.error(e.getMessage(), e);
                        } finally {
                            updatingState.set(false);
                        }
                    });
        });
    }
Gets the current cluster state. If the current state is null or is older
than the expiry time then the cluster state will be updated
asynchronously ready for subsequent calls. The current state is still
returned so could be null
stroom-cluster/stroom-cluster-impl/src/main/java/stroom/cluster/impl/ClusterNodeManagerImpl.java