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
* Return true or false depending on whether the index is ready to be in {@code phase}
     */
    boolean isReadyToTransitionToThisPhase(final String policy, final IndexMetadata indexMetadata, final String phase) {
        final Long lifecycleDate = calculateOriginationMillis(indexMetadata);
        if (lifecycleDate == null) {
            logger.trace("[{}] no index creation or origination date has been set yet", indexMetadata.getIndex().getName());
            return true;
        }
        final TimeValue after = stepRegistry.getIndexAgeForPhase(policy, phase);
        final long now = nowSupplier.getAsLong();
        final long ageMillis = now - lifecycleDate;
        final TimeValue age;
        if (ageMillis >= 0) {
            age = new TimeValue(ageMillis);
        } else if (ageMillis == Long.MIN_VALUE) {
            age = new TimeValue(Long.MAX_VALUE);
        } else {
            age = new TimeValue(-ageMillis);
        }
        if (logger.isTraceEnabled()) {
            logger.trace(
                "[{}] checking for index age to be at least [{}] before performing actions in "
                    + "the \"{}\" phase. Now: {}, lifecycle date: {}, age: [{}{}/{}s]",
                indexMetadata.getIndex().getName(),
                after,
                phase,
                new TimeValue(now).seconds(),
                new TimeValue(lifecycleDate).seconds(),
                ageMillis < 0 ? "-" : "",
                age,