`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
* call exactly one of these methods for every task in its batch.
     */
    interface TaskContext<T extends ClusterStateTaskListener> {
        /**
         * @return the task to be executed.
         */
        T getTask();
        /**
         * Record that the task succeeded.
         * <p>
         * Note that some tasks implement {@link ClusterStateAckListener} and can listen for acks themselves. If so, you may not use this
         * method and must instead call {@link #success(Runnable, ClusterStateAckListener)}, passing the task itself as the {@code
         * clusterStateAckListener} argument.
         *
         * @param onPublicationSuccess An action executed when (if?) the cluster state update succeeds.
         */
        void success(Runnable onPublicationSuccess);
        /**
         * Record that the task succeeded.
         * <p>
         * Note that some tasks implement {@link ClusterStateAckListener} and can listen for acks themselves. If so, you may not use this
         * method and must instead call {@link #success(Consumer, ClusterStateAckListener)}, passing the task itself as the {@code
         * clusterStateAckListener} argument.
         *
         * @param publishedStateConsumer A consumer of the cluster state that was ultimately published.
         *                               <p>
         *                               The consumer should prefer not to use the published state for things like determining the result
         *                               of a task. The task may have been executed as part of a batch, and later tasks in the batch may
         *                               overwrite the results from earlier tasks. Instead the listener should independently capture the
         *                               information it needs to properly process the completion of a cluster state update.