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

org.spf4j.failsafe.RetryPolicy Maven / Gradle / Ivy

Go to download

A continuously growing collection of utilities to measure performance, get better diagnostics, improve performance, or do things more reliably, faster that other open source libraries...

There is a newer version: 8.10.0
Show newest version
/*
 * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Additionally licensed with:
 *
 * 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.spf4j.failsafe;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.CheckReturnValue;
import javax.annotation.ParametersAreNonnullByDefault;
import org.slf4j.Logger;
import org.slf4j.helpers.NOPLogger;
import org.spf4j.base.Throwables;
import org.spf4j.failsafe.concurrent.DefaultFailSafeExecutor;
import org.spf4j.failsafe.concurrent.FailSafeExecutor;

/**
 * @author Zoltan Farkas
 */
@ParametersAreNonnullByDefault
@SuppressFBWarnings("FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY")
public class RetryPolicy> implements SyncRetryExecutor {

  private static final RetryPolicy> DEFAULT;

  static {
    RetryPolicy p;
    String policySupplierClass = System.getProperty("spf4j.failsafe.defaultRetryPolicySupplier");
    if (policySupplierClass == null) {
      p = RetryPolicy.newBuilder()
              .withDefaultThrowableRetryPredicate()
              .withRetryOnException(Exception.class, 2) // will retry any other exception twice.
              .build();
    } else {
      try {
        p = ((Supplier) Class.forName(policySupplierClass).newInstance()).get();
      } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
        throw new ExceptionInInitializerError(ex);
      }
    }
    DEFAULT = p;
  }

  private static final RetryPolicy> NO_RETRY
          = RetryPolicy.newBuilder().build();



  private final TimedSupplier> retryPredSupplier;

  private final int maxExceptionChain;

  RetryPolicy(final TimedSupplier> retryPredicate,
          final int maxExceptionChain) {
    this.retryPredSupplier = retryPredicate;
    this.maxExceptionChain = maxExceptionChain;
  }

  public static > RetryPolicy noRetryPolicy() {
    return (RetryPolicy) NO_RETRY;
  }

  public static > RetryPolicy defaultPolicy() {
    return (RetryPolicy) DEFAULT;
  }


  @Override
  public final  R call(
          final W pwhat, final Class exceptionClass, final long startNanos, final long deadlineNanos)
          throws InterruptedException, TimeoutException, E {
    return (R) SyncRetryExecutor.call(pwhat, getRetryPredicate(startNanos, deadlineNanos),
            exceptionClass, maxExceptionChain);
  }

  public final AsyncRetryExecutor async(final FailSafeExecutor exec) {
    return new AsyncRetryExecutorImpl<>(this, HedgePolicy.DEFAULT, exec);
  }

  public final AsyncRetryExecutor async(final HedgePolicy hedgePolicy, final FailSafeExecutor exec) {
    return new AsyncRetryExecutorImpl<>(this, hedgePolicy, exec);
  }

  public final AsyncRetryExecutor async(final Function hedgePolicy,
          final FailSafeExecutor exec) {
    return new AsyncRetryExecutorImpl<>(c -> this, hedgePolicy, exec);
  }

  public static > AsyncRetryExecutor
         async(final Function> retry,
          final Function hedgePolicy,
          final FailSafeExecutor exec) {
    return new AsyncRetryExecutorImpl<>(retry, hedgePolicy, exec);
  }

  public final AsyncRetryExecutor async() {
    return async(DefaultFailSafeExecutor.instance());
  }

  public final RetryPredicate getRetryPredicate(final long startTimeNanos, final long deadlineNanos) {
    return new TimeoutRetryPredicate(retryPredSupplier.get(startTimeNanos, deadlineNanos), deadlineNanos);
  }

  /**
   * @return string representation of policy.
   */
  @Override
  public String toString() {
    return "RetryPolicy{retryPredicate=" + retryPredSupplier
            + ", maxExceptionChain=" + maxExceptionChain + '}';
  }

  public static final class Builder> {

    private static final int MAX_EX_CHAIN_DEFAULT = Integer.getInteger("spf4j.failsafe.defaultMaxExceptionChain", 5);

    private static final long DEFAULT_MAX_DELAY_NANOS
            = TimeUnit.MILLISECONDS.toNanos(Long.getLong("spf4j.failsafe.defaultMaxRetryDelayMillis", 5000));

    private static final long DEFAULT_INITIAL_DELAY_NANOS
            = Long.getLong("spf4j.failsafe.defaultInitialRetryDelayNanos", 10000);

    private static final int DEFAULT_INITIAL_NODELAY_RETRIES
            = Integer.getInteger("spf4j.failsafe.defaultInitialNoDelayRetries", 3);

    private static final int DEFAULT_MAX_NR_RETRIES
            = Integer.getInteger("spf4j.failsafe.defaultMaxNrRetries", 1000);

    private int maxExceptionChain = MAX_EX_CHAIN_DEFAULT;

    private final List>> resultPredicates;

    private final List>> exceptionPredicates;

    private int nrInitialImmediateRetries;

    private long startDelayNanos;

    private long maxDelayNanos;

    private double jitterFactor;

    private Logger log;

    private Builder() {
      this.nrInitialImmediateRetries = DEFAULT_INITIAL_NODELAY_RETRIES;
      this.startDelayNanos = DEFAULT_INITIAL_DELAY_NANOS;
      this.maxDelayNanos = DEFAULT_MAX_DELAY_NANOS;
      this.jitterFactor = 0.2;
      this.resultPredicates = new ArrayList<>(2);
      this.exceptionPredicates = new ArrayList<>(2);
      this.log = null;
    }

    private Builder(final Builder from) {
      this.nrInitialImmediateRetries = from.nrInitialImmediateRetries;
      this.startDelayNanos = from.startDelayNanos;
      this.maxDelayNanos = from.maxDelayNanos;
      this.jitterFactor = from.jitterFactor;
      this.resultPredicates = new ArrayList(from.resultPredicates);
      this.exceptionPredicates = new ArrayList<>(from.exceptionPredicates);
      this.log = from.log;
    }

    public Builder withRetryLogger(final Logger plog) {
      this.log = plog;
      return this;
    }

    public Builder withoutRetryLogger() {
      this.log = NOPLogger.NOP_LOGGER;
      return this;
    }

    public Builder withDefaultThrowableRetryPredicate() {
      return withDefaultThrowableRetryPredicate(DEFAULT_MAX_NR_RETRIES);
    }

    public Builder withDefaultThrowableRetryPredicate(final int maxNrRetries) {
      return withExceptionPartialPredicate((e, c) -> Throwables.isRetryable(e) ? RetryDecision.retryDefault(c) : null,
              maxNrRetries);
    }

    public Builder withRetryOnException(final Class clasz) {
      return withRetryOnException(clasz, DEFAULT_MAX_NR_RETRIES);
    }

    public Builder withRetryOnException(final Class clasz, final int maxRetries) {
      return withExceptionPartialPredicate((e, c)
              -> clasz.isAssignableFrom(e.getClass())
              ? RetryDecision.retryDefault(c) : null, maxRetries);
    }

    public Builder withRetryOnException(final Class clasz,
            final long maxTime, final TimeUnit tu) {
      return withExceptionPartialPredicate((e, c)
              -> clasz.isAssignableFrom(e.getClass())
              ? RetryDecision.retryDefault(c) : null, maxTime, tu);
    }

    public Builder withExceptionPartialPredicate(final PartialExceptionRetryPredicate predicate,
            final long maxTime, final TimeUnit tu) {
      return withExceptionPartialPredicateSupplier((s, d)
              -> {
        TimeLimitedPartialRetryPredicate p =
                new TimeLimitedPartialRetryPredicate<>(s, d, maxTime, tu, 1.0d, predicate);
        return (PartialExceptionRetryPredicate) (Throwable value, C what) -> p.apply(value, what);
      });
    }

    public Builder withExceptionPartialPredicate(
            final PartialExceptionRetryPredicate predicate) {
      return withExceptionPartialPredicateSupplier(TimedSupplier.constant(predicate));
    }

    public  Builder withExceptionPartialPredicate(final Class clasz,
            final PartialTypedExceptionRetryPredicate predicate) {
      return withExceptionPartialPredicate((e, c) -> {
        if (clasz.isAssignableFrom(e.getClass())) {
          return predicate.getExceptionDecision((E) e, c);
        }
        return null;
      });
    }

    public  Builder withExceptionPartialPredicate(final Class clasz,
            final PartialTypedExceptionRetryPredicate predicate, final int maxRetries) {
      return withExceptionPartialPredicate((e, c) -> {
        if (clasz.isAssignableFrom(e.getClass())) {
          return predicate.getExceptionDecision((E) e, c);
        }
        return null;
      }, maxRetries);
    }

    public Builder withExceptionPartialPredicate(
            final PartialExceptionRetryPredicate predicate,
            final int maxRetries) {
      return withExceptionPartialPredicateSupplier((s, e) -> {
        CountLimitedPartialRetryPredicate p
                = new CountLimitedPartialRetryPredicate(maxRetries, predicate);
        return (Throwable value, C what) -> p.apply(value, what);
      });
    }

    public Builder withExceptionPartialPredicateSupplier(
            final Supplier> predicateSupplier) {
      return withExceptionPartialPredicateSupplier((s, e) -> predicateSupplier.get());
    }

    @Deprecated
    public Builder withExceptionStatefulPartialPredicate(
            final Supplier> predicateSupplier) {
      return Builder.this.withExceptionPartialPredicateSupplier(predicateSupplier);
    }

    public Builder withExceptionPartialPredicateSupplier(
            final TimedSupplier> predicateSupplier) {
      exceptionPredicates.add(predicateSupplier);
      return this;
    }

    @Deprecated
    public Builder withExceptionStatefulPartialPredicate(
            final TimedSupplier> predicateSupplier) {
      return withExceptionPartialPredicateSupplier(predicateSupplier);
    }


    public Builder withRetryOnResult(final T result, final int maxRetries) {
      return withResultPartialPredicate((r, c)
              -> Objects.equals(result, r)
              ? RetryDecision.retryDefault(c) : null, maxRetries);
    }


    public Builder withResultPartialPredicate(
            final PartialResultRetryPredicate predicate) {
      resultPredicates.add(TimedSupplier.constant(predicate));
      return this;
    }

    @SuppressWarnings("unchecked")
    public  Builder withResultPartialPredicate(
            final Class clasz,
            final PartialResultRetryPredicate> predicate) {
      resultPredicates.add(TimedSupplier.constant((o, c)
              -> (o != null && clasz.isAssignableFrom(o.getClass()))
                      ? predicate.getDecision((A) o, (Callable) c) : null
      ));
      return this;
    }

    public Builder withResultPartialPredicate(
            final PartialResultRetryPredicate predicate,
            final int maxRetries) {
      return withResultPartialPredicateSupplier((s, e) -> {
        CountLimitedPartialRetryPredicate p = new CountLimitedPartialRetryPredicate<>(maxRetries, predicate);
        return (T value, C what) -> p.apply(value, what);
      });
    }

    public Builder withResultPartialPredicateSupplier(
            final Supplier> predicateSupplier) {
      return withResultPartialPredicateSupplier((s, e) -> predicateSupplier.get());
    }

    @Deprecated
    public Builder withResultStatefulPartialPredicate(
            final Supplier> predicateSupplier) {
      return Builder.this.withResultPartialPredicateSupplier(predicateSupplier);
    }

    public Builder withResultPartialPredicateSupplier(
            final TimedSupplier> predicateSupplier) {
      resultPredicates.add(predicateSupplier);
      return this;
    }

    @Deprecated
    public Builder withResultStatefulPartialPredicate(
            final TimedSupplier> predicateSupplier) {
      return withResultPartialPredicateSupplier(predicateSupplier);
    }

    public Builder withJitterFactor(final double jitterfactor) {
      if (jitterFactor > 1 || jitterFactor < 0) {
        throw new IllegalArgumentException("Invalid jitter factor " + jitterfactor);
      }
      this.jitterFactor = jitterfactor;
      return this;
    }

    /**
     * @deprecated use withInitialImmediateRetries instead.
     */
    @Deprecated
    public Builder withInitialRetries(final int retries) {
      this.nrInitialImmediateRetries = retries;
      return this;
    }

    public Builder withInitialImmediateRetries(final int retries) {
      this.nrInitialImmediateRetries = retries;
      return this;
    }

    public Builder withInitialDelay(final long delay, final TimeUnit unit) {
      this.startDelayNanos = unit.toNanos(delay);
      return this;
    }

    public Builder withMaxDelay(final long delay, final TimeUnit unit) {
      this.maxDelayNanos = unit.toNanos(delay);
      return this;
    }

    public Builder withMaxExceptionChain(final int maxExChain) {
      maxExceptionChain = maxExChain;
      return this;
    }

    @CheckReturnValue
    public Builder copy() {
      return new Builder<>(this);
    }

    @CheckReturnValue
    public RetryPolicy build() {
      TimedSupplier[] rps = resultPredicates.toArray(new TimedSupplier[resultPredicates.size()]);
      TimedSupplier[] eps = exceptionPredicates.toArray(new TimedSupplier[exceptionPredicates.size()]);
      TimedSupplier> retryPredicate
              = (s, e) -> new DefaultRetryPredicate(log, s, e, () -> new TypeBasedRetryDelaySupplier<>(
              x -> new JitteredDelaySupplier(new FibonacciRetryDelaySupplier(nrInitialImmediateRetries,
                      startDelayNanos, maxDelayNanos), jitterFactor)), rps, eps);
      return new RetryPolicy<>(retryPredicate, maxExceptionChain);
    }

    @CheckReturnValue
    public AsyncRetryExecutor buildAsync() {
      return buildAsync(DefaultFailSafeExecutor.instance());
    }

    @CheckReturnValue
    public AsyncRetryExecutor buildAsync(final FailSafeExecutor es) {
       return build().async(es);
    }

  }

  /**
   * Create a retry policy builder.
   *
   * @param  the Type returned by the retried callables.
   * @param  the type of the Callable's returned.
   * @return
   */
  @CheckReturnValue
  public static > Builder newBuilder() {
    return new Builder<>();
  }

  /**
   * Create a retry policy builder copy.
   *
   * @param  the Type returned by the retried callables.
   * @param  the type of the Callable's returned.
   * @return
   */
  @CheckReturnValue
  public static > Builder newBuilder(final Builder builder) {
    return new Builder<>(builder);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy