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

org.zalando.riptide.autoconfigure.FailsafePluginFactory Maven / Gradle / Ivy

The newest version!
package org.zalando.riptide.autoconfigure;

import dev.failsafe.CircuitBreaker;
import dev.failsafe.CircuitBreakerBuilder;
import dev.failsafe.RetryPolicy;
import dev.failsafe.RetryPolicyBuilder;
import dev.failsafe.Timeout;
import dev.failsafe.function.ContextualSupplier;
import org.springframework.http.client.ClientHttpResponse;
import org.zalando.riptide.Plugin;
import org.zalando.riptide.autoconfigure.RiptideProperties.Client;
import org.zalando.riptide.autoconfigure.RiptideProperties.Retry;
import org.zalando.riptide.autoconfigure.RiptideProperties.Retry.Backoff;
import org.zalando.riptide.failsafe.BackupRequest;
import org.zalando.riptide.failsafe.CircuitBreakerListener;
import org.zalando.riptide.failsafe.CompositeDelayFunction;
import org.zalando.riptide.failsafe.FailsafePlugin;
import org.zalando.riptide.failsafe.RateLimitResetDelayFunction;
import org.zalando.riptide.failsafe.RequestPolicies;
import org.zalando.riptide.failsafe.RetryAfterDelayFunction;
import org.zalando.riptide.failsafe.RetryException;
import org.zalando.riptide.failsafe.RetryRequestPolicy;
import org.zalando.riptide.failsafe.TaskDecorator;
import org.zalando.riptide.idempotency.IdempotencyPredicate;

import javax.annotation.Nullable;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import static java.time.Clock.systemUTC;
import static java.time.temporal.ChronoUnit.MILLIS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.zalando.riptide.failsafe.CheckedPredicateConverter.toCheckedPredicate;
import static org.zalando.riptide.failsafe.TaskDecorator.composite;
import static org.zalando.riptide.faults.Predicates.alwaysTrue;
import static org.zalando.riptide.faults.TransientFaults.transientConnectionFaults;
import static org.zalando.riptide.faults.TransientFaults.transientSocketFaults;

@SuppressWarnings("unused")
final class FailsafePluginFactory {

    private FailsafePluginFactory() {

    }

    public static Plugin createCircuitBreakerPlugin(
            final CircuitBreaker breaker,
            final List decorators,
            @Nullable final ExecutorService executorService) {

        return new FailsafePlugin()
                .withExecutor(executorService)
                .withPolicy(breaker)
                .withDecorator(composite(decorators));
    }

    public static CircuitBreaker createCircuitBreaker(
            final Client client,
            final CircuitBreakerListener listener) {

        final CircuitBreakerBuilder breakerBuilder = CircuitBreaker.builder();

        Optional.ofNullable(client.getCircuitBreaker().getFailureThreshold())
                .ifPresent(threshold -> threshold.applyTo(breakerBuilder::withFailureThreshold));

        Optional.ofNullable(client.getCircuitBreaker().getFailureRateThreshold())
                        .ifPresent(threshold -> threshold.applyTo(breakerBuilder::withFailureRateThreshold));

        Optional.ofNullable(client.getCircuitBreaker().getDelay())
                .ifPresent(delay -> delay.applyTo(breakerBuilder::withDelay));

        Optional.ofNullable(client.getCircuitBreaker().getSuccessThreshold())
                .ifPresent(threshold -> threshold.applyTo(breakerBuilder::withSuccessThreshold));

        breakerBuilder.withDelayFn(delayFunction())
                .onOpen(event -> listener.onOpen())
                .onHalfOpen(event -> listener.onHalfOpen())
                .onClose(event -> listener.onClose());

        return breakerBuilder.build();
    }

    public static Plugin createRetryFailsafePlugin(
            final Client client,
            final List decorators,
            @Nullable final ExecutorService executorService) {

        if (client.getTransientFaultDetection().getEnabled()) {
            return new FailsafePlugin()
                    .withExecutor(executorService)
                    .withPolicy(new RetryRequestPolicy(getRetryPolicyBuilder(client)
                            .handleIf(toCheckedPredicate(transientSocketFaults()))
                            .build())
                            .withPredicate(new IdempotencyPredicate()))
                    .withPolicy(new RetryRequestPolicy(getRetryPolicyBuilder(client)
                            .handleIf(toCheckedPredicate(transientConnectionFaults()))
                            .build())
                            .withPredicate(alwaysTrue()))
                    .withPolicy(new RetryRequestPolicy(getRetryPolicyBuilder(client).handle(RetryException.class).build()))
                    .withDecorator(composite(decorators));
        } else {
            return new FailsafePlugin()
                    .withExecutor(executorService)
                    .withPolicy(new RetryRequestPolicy(getRetryPolicyBuilder(client).handle(RetryException.class).build()))
                    .withDecorator(composite(decorators));
        }
    }

    private static RetryPolicyBuilder getRetryPolicyBuilder(Client client) {
        final RetryPolicyBuilder policyBuilder = RetryPolicy.builder();

        final Retry config = client.getRetry();

        Optional.ofNullable(config.getFixedDelay())
                .ifPresent(delay -> delay.applyTo((Consumer)policyBuilder::withDelay));

        Optional.ofNullable(config.getBackoff())
                .filter(Backoff::getEnabled)
                .ifPresent(backoff -> {
                    final TimeSpan delay = backoff.getDelay();
                    final TimeSpan maxDelay = backoff.getMaxDelay();
                    final TimeUnit unit = MILLISECONDS;

                    @Nullable final Double delayFactor = backoff.getDelayFactor();

                    if (delayFactor == null) {
                        policyBuilder.withBackoff(delay.to(unit), maxDelay.to(unit), MILLIS);
                    } else {
                        policyBuilder.withBackoff(delay.to(unit), maxDelay.to(unit), MILLIS, delayFactor);
                    }
                });

        Optional.ofNullable(config.getMaxRetries())
                .ifPresent(policyBuilder::withMaxRetries);

        Optional.ofNullable(config.getMaxDuration())
                .ifPresent(duration -> duration.applyTo(policyBuilder::withMaxDuration));

        Optional.ofNullable(config.getJitterFactor())
                .ifPresent(policyBuilder::withJitter);

        Optional.ofNullable(config.getJitter())
                .ifPresent(jitter -> jitter.applyTo(policyBuilder::withJitter));

        policyBuilder.withDelayFn(delayFunction());
        return policyBuilder;
    }

    public static Plugin createBackupRequestPlugin(
            final Client client,
            final List decorators,
            @Nullable final ExecutorService executorService) {

        final TimeSpan delay = client.getBackupRequest().getDelay();

        return new FailsafePlugin()
                .withExecutor(executorService)
                .withPolicy(RequestPolicies.of(
                        new BackupRequest<>(delay.getAmount(), delay.getUnit()),
                        new IdempotencyPredicate()))
                .withDecorator(composite(decorators));
    }

    public static Plugin createTimeoutPlugin(
            final Client client,
            final List decorators,
            @Nullable final ExecutorService executorService) {

        final Duration timeout = client.getTimeouts().getGlobal().toDuration();

        return new FailsafePlugin()
                .withExecutor(executorService)
                .withPolicy(
                        Timeout.builder(timeout)
                                .withInterrupt()
                                .build()
                )
                .withDecorator(composite(decorators));
    }

    private static ContextualSupplier delayFunction() {
        return new CompositeDelayFunction<>(Arrays.asList(
                new RetryAfterDelayFunction(systemUTC()),
                new RateLimitResetDelayFunction(systemUTC())
        ));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy