`GatewayService` implements logic for delaying state recovery in the cluster until enough data nodes have joined and/or some timeout has occurred. The implementation is pretty old and doesn't make use of much modern infrastructure for doing this kind of thing, making it pretty hard to follow and to improve (see e.g. #86588). We should clean it up and simplify it.
server/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java
void onFailure(Exception failure);
        /**
         * Creates a context which captures any response headers (e.g. deprecation warnings) to be fed to the task's listener on completion.
         */
        Releasable captureResponseHeaders();
    }
    /**
     * Encapsulates the context in which a batch of tasks executes.
     *
     * @param initialState The initial cluster state on which the tasks should be executed.
     * @param taskContexts A {@link TaskContext} for each task in the batch. Implementations must complete every context in the list.
     * @param dropHeadersContextSupplier Supplies a context (a resource for use in a try-with-resources block) which captures and drops any
     *                                   emitted response headers, for cases where things like deprecation warnings may be emitted but
     *                                   cannot be associated with any specific task.
     */
    record BatchExecutionContext<T extends ClusterStateTaskListener>(
        ClusterState initialState,
        List<? extends TaskContext<T>> taskContexts,
        Supplier<Releasable> dropHeadersContextSupplier
    ) {
        /**
         * Creates a context (a resource for use in a try-with-resources block) which captures and drops any emitted response headers, for
         * cases where things like deprecation warnings may be emitted but cannot be associated with any specific task.
         */
        public Releasable dropHeadersContext() {
            return dropHeadersContextSupplier.get();
        }
    }
}