All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cloudfoundry.util.DelayUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cloudfoundry.util;

import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;
import java.util.function.Function;
import org.atteo.evo.inflector.English;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * Utilities for delaying progress
 */
public final class DelayUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger("cloudfoundry-client.delay");

    private DelayUtils() {}

    /**
     * Implements an exponential backoff delay for use with {@link Mono#repeatWhenEmpty(Function)}
     *
     * @param minimum the minimum duration
     * @param maximum the maximum duration
     * @param timeout the maximum amount of time to delay for
     * @return a delayed {@link Publisher}
     */
    public static Function, Publisher> exponentialBackOff(
            Duration minimum, Duration maximum, Duration timeout) {
        Instant finish = Instant.now().plus(timeout);
        return iterations -> getDelay(minimum, maximum, finish, iterations);
    }

    /**
     * Implements a fixed delay for use with {@link Mono#repeatWhenEmpty(Function)}
     *
     * @param duration the duration of the delay
     * @return a delayed {@link Publisher}
     */
    public static Function, Publisher> fixed(Duration duration) {
        return iterations ->
                iterations.flatMap(
                        iteration -> Mono.delay(duration).doOnSubscribe(logDelay(duration)), 1);
    }

    /**
     * Implements an instant (no delay) for use with {@link Mono#repeatWhenEmpty(Function)}
     *
     * @return an instant (no delay) {@link Publisher}
     */
    public static Function, Publisher> instant() {
        return iterations ->
                iterations.flatMap(
                        iteration -> Mono.just(0L).doOnSubscribe(logDelay(Duration.ZERO)), 1);
    }

    private static Duration calculateDuration(Duration minimum, Duration maximum, Long iteration) {
        Duration candidate = minimum.multipliedBy((long) Math.pow(2, iteration));
        return min(candidate, maximum);
    }

    private static Flux getDelay(
            Duration minimum, Duration maximum, Instant finish, Flux iterations) {
        return iterations
                .map(iteration -> calculateDuration(minimum, maximum, iteration))
                .concatMap(
                        delay -> {
                            if (Instant.now().isAfter(finish)) {
                                return Mono.error(new DelayTimeoutException());
                            }

                            return Mono.delay(delay).doOnSubscribe(logDelay(delay));
                        });
    }

    private static Consumer logDelay(Duration delay) {
        return subscription -> {
            int seconds = (int) delay.getSeconds();
            if (seconds > 0) {
                LOGGER.debug("Delaying {} {}", seconds, English.plural("second", seconds));
                return;
            }

            int milliseconds = (int) delay.toMillis();
            LOGGER.debug(
                    "Delaying {} {}", milliseconds, English.plural("millisecond", milliseconds));
        };
    }

    private static Duration min(Duration a, Duration b) {
        return (a.compareTo(b) <= 0) ? a : b;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy