In https://github.com/elastic/elasticsearch/pull/78547 we introduced batching for the ILM master tasks that occur on the happy path. However if a high-shard-count cluster encounters problems while doing ILM-related things—perhaps some nodes are temporarily unavailable for taking a snapshot—then we process the resulting `ilm-retry-failed-step` and `ilm-move-to-error-step` tasks one-by-one which can significantly delay the cluster's recovery from its problems.

We should batch these things together too.

It looks like we also enqueue duplicate `ilm-retry-failed-step` on each poll interval too, although we do appear to treat the duplicates as no-ops at execution time.

Relates https://github.com/elastic/elasticsearch/issues/77466
x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunner.java
moveToStep(indexMetadata.getIndex(), policy, currentStep.getKey(), currentStep.getNextStepKey());
            }
        } else if (currentStep instanceof AsyncWaitStep) {
            logger.debug("[{}] running periodic policy with current-step [{}]", index, currentStep.getKey());
            ((AsyncWaitStep) currentStep).evaluateCondition(metadata, indexMetadata.getIndex(), new AsyncWaitStep.Listener() {
                @Override
                public void onResponse(boolean conditionMet, ToXContentObject stepInfo) {
                    logger.trace("cs-change-async-wait-callback, [{}] current-step: {}", index, currentStep.getKey());
                    if (conditionMet) {
                        moveToStep(indexMetadata.getIndex(), policy, currentStep.getKey(), currentStep.getNextStepKey());
                    } else if (stepInfo != null) {
                        setStepInfo(indexMetadata.getIndex(), policy, currentStep.getKey(), stepInfo);
                    }
                }
                @Override
                public void onFailure(Exception e) {
                    moveToErrorStep(indexMetadata.getIndex(), policy, currentStep.getKey(), e);
                }
            }, TimeValue.MAX_VALUE);
        } else {
            logger.trace("[{}] ignoring non periodic step execution from step transition [{}]", index, currentStep.getKey());
        }
    }
    /**
     * Given the policy and index metadata for an index, this moves the index's
     * execution state to the previously failed step, incrementing the retry
     * counter.
     */
    void onErrorMaybeRetryFailedStep(String policy, IndexMetadata indexMetadata) {
        String index = indexMetadata.getIndex().getName();