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

com.couchbase.client.core.retry.reactor.Retry Maven / Gradle / Ivy

/*
 * Copyright (c) 2017 Pivotal Software Inc, All Rights Reserved.
 *
 * 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 com.couchbase.client.core.retry.reactor;

import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;

import java.time.Duration;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Retry function that may be used with {@link Flux#retryWhen(Function)} and {@link
 * Mono#retryWhen(Function)}.
 * 

* Each change in configuration returns a new instance (copy configuration), which * makes {@link Retry} suitable for creating configuration templates that can be fine * tuned for specific cases without impacting the original general use-case configuration. *

* Example usage: * *


 *   retry = Retry.anyOf(IOException.class)
 *                 .randomBackoff(Duration.ofMillis(100), Duration.ofSeconds(60))
 *                 .withApplicationContext(appContext)
 *                 .doOnRetry(context -> context.applicationContext().rollback());
 *   flux.retryWhen(retry);
 * 
* * @param Application context type */ public interface Retry extends Function, Publisher> { /** * Returns a retry function that retries any exception, once. * More constraints may be added using {@link #retryMax(long)} or {@link #timeout(Duration)}. * * @return retry function that retries on any exception */ static Retry any() { return DefaultRetry.create(context -> true); } /** * Returns a retry function that retries errors resulting from any of the * specified exceptions, once. * More constraints may be added using {@link #retryMax(long)} * or {@link #timeout(Duration)}. * * @param retriableExceptions Exceptions that may be retried * @return retry function that retries indefinitely, only for specified exceptions */ @SafeVarargs static Retry anyOf(Class... retriableExceptions) { Predicate> predicate = context -> { Throwable exception = context.exception(); if (exception == null) return true; for (Class clazz : retriableExceptions) { if (clazz.isInstance(exception)) return true; } return false; }; return DefaultRetry.create(predicate); } /** * Returns a retry function that retries errors resulting from all exceptions except * the specified non-retriable exceptions, once. * More constraints may be added using * {@link #retryMax(long)} or {@link #timeout(Duration)}. * * @param nonRetriableExceptions exceptions that may not be retried * @return retry function that retries all exceptions except the specified non-retriable exceptions. */ @SafeVarargs static Retry allBut(final Class... nonRetriableExceptions) { Predicate> predicate = context -> { Throwable exception = context.exception(); if (exception == null) return true; for (Class clazz : nonRetriableExceptions) { if (clazz.isInstance(exception)) return false; } return true; }; return DefaultRetry.create(predicate); } /** * Retry function that retries only if the predicate returns true, with no limit to * the number of attempts. * @param predicate Predicate that determines if next retry is performed * @return Retry function with predicate */ static Retry onlyIf(Predicate> predicate) { return DefaultRetry.create(predicate).retryMax(Long.MAX_VALUE); } /** * Returns a retry function with an application context that may be * used to perform any rollbacks before a retry. This application * context is provided to any retry predicate {@link #onlyIf(Predicate)}, * custom backoff function {@link #backoff(Backoff)} and retry * callback {@link #doOnRetry(Consumer)}. All other properties of * this retry function are retained in the returned instance. * * @param applicationContext Application context * @return retry function with associated application context */ Retry withApplicationContext(T applicationContext); /** * Returns a retry function that invokes the provided onRetry * callback before every retry. The {@link RetryContext} provided * to the callback contains the iteration and the any application * context set using {@link #withApplicationContext(Object)}. * All other properties of this retry function are retained in the * returned instance. * * @param onRetry callback to invoke before retries * @return retry function with callback */ Retry doOnRetry(Consumer> onRetry); /** * Retry function that retries once. * @return Retry function for one retry */ default Retry retryOnce() { return retryMax(1); } /** * Retry function that retries n times. * @param maxRetries number of retries * @return Retry function for n retries */ Retry retryMax(long maxRetries); /** * Returns a retry function with timeout. The timeout starts from * the instant that this function is applied, and the function keeps retrying * until the timeout expires (or until the configured maximum number of attempts, if * it has been set). All other properties of this retry function are retained in * the returned instance. * @param timeout timeout after which no new retries are initiated * @return retry function with global timeout */ Retry timeout(Duration timeout); /** * Returns a retry function with backoff delay. * All other properties of this retry function are retained in the * returned instance. * * @param backoff the backoff function to determine backoff delay * @return retry function with backoff */ Retry backoff(Backoff backoff); /** * Returns a retry function that applies jitter to the backoff delay. * All other properties of this retry function are retained in the * returned instance. * * @param jitter Jitter function to randomize backoff delay * @return retry function with jitter for backoff */ Retry jitter(Jitter jitter); /** * Returns a retry function that uses the scheduler provided for * backoff delays. All other properties of this retry function * are retained in the returned instance. * @param scheduler the scheduler for backoff delays * @return retry function with backoff scheduler */ Retry withBackoffScheduler(Scheduler scheduler); /** * Returns a retry function with no backoff delay. This is the default. * All other properties of this retry function are retained in the * returned instance. * * @return retry function with no backoff delay */ default Retry noBackoff() { return backoff(Backoff.zero()); } /** * Returns a retry function with fixed backoff delay. * All other properties of this retry function are retained in the * returned instance. * * @param backoffInterval fixed backoff delay applied before every retry * @return retry function with fixed backoff delay */ default Retry fixedBackoff(Duration backoffInterval) { return backoff(Backoff.fixed(backoffInterval)); } /** * Returns a retry function with exponential backoff delay. * All other properties of this retry function are retained in the * returned instance. *

* Retries are performed after a backoff interval of firstBackoff * (2 ** n) * where n is the next iteration number. If maxBackoff is not null, the maximum * backoff applied will be limited to maxBackoff. * * @param firstBackoff the delay for the first backoff, which is also used as the coefficient for subsequent backoffs * @param maxBackoff the maximum backoff delay before a retry * @return retry function with exponential backoff delay */ default Retry exponentialBackoff(Duration firstBackoff, Duration maxBackoff) { return backoff(Backoff.exponential(firstBackoff, maxBackoff, 2, false)); } /** * Returns a retry function with full jitter backoff strategy. * All other properties of this retry function are retained in the * returned instance. *

* Retries are performed after a random backoff interval between firstBackoff and * firstBackoff * (2 ** n) where n is the next iteration number. If maxBackoff * is not null, the maximum backoff applied will be limited to maxBackoff. * * @param firstBackoff the delay for the first backoff, which is also used as the coefficient for subsequent backoffs * @param maxBackoff the maximum backoff delay before a retry * @return retry function with full jitter backoff strategy */ default Retry exponentialBackoffWithJitter(Duration firstBackoff, Duration maxBackoff) { return backoff(Backoff.exponential(firstBackoff, maxBackoff, 2, false)).jitter(Jitter.random()); } /** * Returns a retry function with random de-correlated jitter backoff strategy. * All other properties of this retry function are retained in the * returned instance. *

* Retries are performed after a backoff interval of random_between(firstBackoff, prevBackoff * 3), * with a minimum value of firstBackoff. If maxBackoff * is not null, the maximum backoff applied will be limited to maxBackoff. * * @param firstBackoff the delay for the first backoff, also used as minimum backoff * @param maxBackoff the maximum backoff delay before a retry * @return retry function with de-correlated jitter backoff strategy */ default Retry randomBackoff(Duration firstBackoff, Duration maxBackoff) { return backoff(Backoff.exponential(firstBackoff, maxBackoff, 3, true)).jitter(Jitter.random()); } /** * Transforms the source into a retrying {@link Flux} based on the properties * configured for this function. *

* Example usage: *


	 *    retry = Retry.anyOf(IOException.class)
	 *                 .withApplicationContext(appContext)
	 *                 .doOnRetry(context -> context.applicationContext().rollback())
	 *                 .exponentialBackoff(Duration.ofMillis(100), Duration.ofSeconds(60));
	 *    flux.as(retry);
	 * 
* * @param source the source publisher * @return {@link Flux} with the retry properties of this retry function */ default Flux apply(Publisher source) { return Flux.from(source).retryWhen(this); } }