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

com.rabbitmq.client.amqp.impl.AsyncRetry Maven / Gradle / Ivy

Go to download

The RabbitMQ AMQP 1.0 Java client library defines an API to access RabbitMQ with the AMQP 1.0 protocol.

There is a newer version: 0.1.0
Show newest version
// Copyright (c) 2020-2023 Broadcom. All Rights Reserved.
// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
//
// 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.
//
// If you have any questions regarding licensing, please contact us at
// [email protected].
package com.rabbitmq.client.amqp.impl;

import static com.rabbitmq.client.amqp.impl.Utils.namedRunnable;

import com.rabbitmq.client.amqp.BackOffDelayPolicy;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class AsyncRetry {

  private static final Logger LOGGER = LoggerFactory.getLogger(AsyncRetry.class);

  private final CompletableFuture completableFuture;

  private AsyncRetry(
      Callable task,
      String description,
      ScheduledExecutorService scheduler,
      BackOffDelayPolicy delayPolicy,
      Predicate retry) {
    this.completableFuture = new CompletableFuture<>();
    AtomicReference retryableTaskReference = new AtomicReference<>();
    AtomicInteger attempts = new AtomicInteger(0);
    Runnable retryableTask =
        namedRunnable(
            () -> {
              if (Thread.currentThread().isInterrupted()) {
                LOGGER.debug("Task '{}' interrupted, failing future", description);
                this.completableFuture.completeExceptionally(new CancellationException());
                return;
              }
              try {
                V result = task.call();
                LOGGER.debug("Task '{}' succeeded, completing future", description);
                completableFuture.complete(result);
              } catch (Exception e) {
                int attemptCount = attempts.getAndIncrement();
                if (retry.test(e)) {
                  if (delayPolicy.delay(attemptCount).equals(BackOffDelayPolicy.TIMEOUT)) {
                    LOGGER.debug(
                        "Retryable attempts for task '{}' timed out, failing future", description);
                    this.completableFuture.completeExceptionally(new RetryTimeoutException());
                  } else {
                    LOGGER.debug(
                        "Retryable exception ({}) for task '{}', scheduling another attempt",
                        e.getClass().getSimpleName(),
                        description);
                    schedule(
                        scheduler, retryableTaskReference.get(), delayPolicy.delay(attemptCount));
                  }
                } else {
                  LOGGER.debug(
                      "Non-retryable exception for task '{}', failing future", description);
                  this.completableFuture.completeExceptionally(e);
                }
              }
            },
            description);
    retryableTaskReference.set(retryableTask);
    Duration initialDelay = delayPolicy.delay(attempts.getAndIncrement());
    LOGGER.debug("Scheduling task '{}' with policy {}", description, delayPolicy);
    if (initialDelay.isZero()) {
      retryableTask.run();
    } else {
      schedule(scheduler, retryableTaskReference.get(), initialDelay);
    }
  }

  private static void schedule(
      ScheduledExecutorService scheduler, Runnable command, Duration delay) {
    try {
      scheduler.schedule(command, delay.toMillis(), TimeUnit.MILLISECONDS);
    } catch (RuntimeException e) {
      LOGGER.debug("Error while scheduling command", e);
    }
  }

  static  AsyncRetryBuilder asyncRetry(Callable task) {
    return new AsyncRetryBuilder<>(task);
  }

  static class AsyncRetryBuilder {

    private final Callable task;
    private String description = "";
    private ScheduledExecutorService scheduler;
    private BackOffDelayPolicy delayPolicy = BackOffDelayPolicy.fixed(Duration.ofSeconds(1));
    private Predicate retry = e -> true;

    AsyncRetryBuilder(Callable task) {
      this.task = task;
    }

    AsyncRetryBuilder scheduler(ScheduledExecutorService scheduler) {
      this.scheduler = scheduler;
      return this;
    }

    AsyncRetryBuilder delay(Duration delay) {
      this.delayPolicy = BackOffDelayPolicy.fixed(delay);
      return this;
    }

    AsyncRetryBuilder delayPolicy(BackOffDelayPolicy delayPolicy) {
      this.delayPolicy = delayPolicy;
      return this;
    }

    AsyncRetryBuilder retry(Predicate predicate) {
      this.retry = predicate;
      return this;
    }

    AsyncRetryBuilder description(String description, Object... args) {
      this.description = String.format(description, args);
      return this;
    }

    CompletableFuture build() {
      return new AsyncRetry<>(task, description, scheduler, delayPolicy, retry).completableFuture;
    }
  }

  static class RetryTimeoutException extends RuntimeException {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy