Giter VIP home page Giter VIP logo

retry-master's Introduction

RetryMaster

stability-beta GitHub Workflow Status (with branch) codecov Packagist PHP Version GitHub

RetryMaster is a flexible and extensible PHP library for handling operation retries. It provides a simple, declarative way of managing operations that might fail due to transient issues. By using RetryMaster, you can easily implement robust retry logic with customizable policies for when and how to perform retries.

Features

  • Flexible Retry Policies: Choose from a variety of built-in retry policies or create your own. You can easily control how and when retries are performed based on the type and number of exceptions, timeout, maximum attempts and more.
  • Configurable Backoff Policies: Control the delay between retries using various backoff strategies including fixed delay, exponential backoff or custom backoff logic.
  • Detailed Retry Statistics: Collect and access detailed statistics about your retry operations, such as total attempts, successful attempts, failed attempts and total sleep time.
  • Easy-to-Use Retry Template: Use the RetryTemplate to execute operations with retry logic. Simply provide the operation logic and the RetryTemplate handles the rest.
  • Custom Retry and Recovery Callbacks: Define custom logic to execute on each retry attempt and when all retries fail.

Table of Contents

Installation

RetryMaster is available as a Composer package. You can add it to your project by running the following command in your terminal:

composer require ilicmiljan/retry-master

This will add RetryMaster to your project's dependencies and download the package to your vendor directory.

After installation, you can use RetryMaster classes by adding the appropriate use statements at the top of your PHP files. For example:

use IlicMiljan\RetryMaster\RetryTemplate;
use IlicMiljan\RetryMaster\Policy\Retry\MaxAttemptsRetryPolicy;
use IlicMiljan\RetryMaster\Policy\Backoff\ExponentialBackoffPolicy;

Be sure to run composer dump-autoload if you're not using a framework that does this automatically.

Usage

Using RetryMaster in your PHP application involves setting up a RetryTemplate and executing your operation using this template. With the introduction of a builder and interfaces, you can use RetryTemplateBuilder to conveniently create a RetryTemplate. You can customize the retry logic by specifying retry and backoff policies when constructing the RetryTemplate.

Here is a basic example:

use IlicMiljan\RetryMaster\RetryTemplateBuilder;
use IlicMiljan\RetryMaster\Callback\RetryCallback;
use IlicMiljan\RetryMaster\Context\RetryContext;

$retryTemplate = (new RetryTemplateBuilder())->build();

$retryCallback = new class implements RetryCallback {
    public function doWithRetry(RetryContext $context) {
        // Your operation goes here. For example:
        // return $this->repository->find($id);
    }
};

$result = $retryTemplate->execute($retryCallback);

In this example, the operation will be retried up to three times (the default maximum attempts) if an exception is thrown. Between each attempt, there will be a fixed delay of one second (the default backoff policy).

Customizing Retry Logic

You can specify custom retry and backoff policies when creating the RetryTemplate using the builder:

use IlicMiljan\RetryMaster\RetryTemplateBuilder;
use IlicMiljan\RetryMaster\Policy\Retry\MaxAttemptsRetryPolicy;
use IlicMiljan\RetryMaster\Policy\Backoff\UniformRandomBackoffPolicy;

$retryPolicy = new MaxAttemptsRetryPolicy(5);
$backoffPolicy = new UniformRandomBackoffPolicy(500, 1500);

$retryTemplate = (new RetryTemplateBuilder())
                    ->setRetryPolicy($retryPolicy)
                    ->setBackoffPolicy($backoffPolicy)
                    ->build();

In this example, the operation will be retried up to five times, and the delay between attempts will be a random number of milliseconds between 500 and 1500.

Handling Retry Failures

You can provide a recovery callback to handle cases when all retry attempts fail:

use IlicMiljan\RetryMaster\RetryTemplateBuilder;
use IlicMiljan\RetryMaster\Callback\RetryCallback;
use IlicMiljan\RetryMaster\Callback\RecoveryCallback;
use IlicMiljan\RetryMaster\Context\RetryContext;

$retryTemplate = (new RetryTemplateBuilder())->build();

$retryCallback = new class implements RetryCallback {
    public function doWithRetry(RetryContext $context) {
        // Your operation goes here.
    }
};

$recoveryCallback = new class implements RecoveryCallback {
    public function recover(RetryContext $context) {
        // Your recovery logic goes here. For example:
        // return $this->fallbackRepository->find($id);
    }
};

$result = $retryTemplate->executeWithRecovery($retryCallback, $recoveryCallback);

Gathering Retry Statistics

You can retrieve statistics about retry operations from the RetryTemplate:

$retryStatistics = $retryTemplate->getRetryStatistics();

echo 'Total attempts: ' . $retryStatistics->getTotalAttempts() . "\n";
echo 'Successful attempts: ' . $retryStatistics->getSuccessfulAttempts() . "\n";
echo 'Failed attempts: ' . $retryStatistics->getFailedAttempts() . "\n";
echo 'Total sleep time: ' . $retryStatistics->getTotalSleepTimeMilliseconds() . "ms\n";

For more usage examples, please refer to the inline comments in each class.

Documentation

Overview

RetryMaster is designed to facilitate the implementation of retry operations in your PHP applications. It provides a set of tools for managing retry logic, including customizable retry and backoff policies and detailed retry statistics.

Retry Policies

A retry policy determines whether an operation should be retried after a failure. RetryMaster includes several built-in retry policies, such as:

  • AlwaysRetryPolicy: This policy always allows a retry, irrespective of the type of exception or the number of attempts so far. It can be used in scenarios where you want to keep retrying indefinitely until the operation succeeds. However, it should be used with caution, as it can potentially lead to an infinite loop if the operation always fails.

  • CompositeRetryPolicy: This policy delegates the decision whether to retry to multiple other policies. It allows combining multiple policies in an optimistic or pessimistic manner. In optimistic mode (the default), the operation is retried if any of the policies allows it. In pessimistic mode, the operation is retried only if all policies allow it.

  • MaxAttemptsRetryPolicy: This policy allows an operation to be retried a specified maximum number of times. It is useful in scenarios where you want to limit the number of retry attempts for an operation to avoid excessive retries.

  • NeverRetryPolicy: This policy disallows any retry attempts, regardless of the operation or its result. It is useful in scenarios where you do not want any retries to be performed for a certain operation, irrespective of whether it fails or not.

  • NonRepeatingExceptionRetryPolicy: This policy allows a retry only if the exception type thrown by the last failed attempt is different from the current exception type. It is beneficial in scenarios where an operation is expected to fail repeatedly with the same type of exception, and retrying would not change the outcome.

  • SimpleRetryPolicy: This policy retries a failed operation a fixed number of times, and for a specific set of exceptions. It is configurable with a maxAttempts property and a retryableExceptions list. The shouldRetry method will return true if the exception is either in the list of retryable exceptions or if the list is empty, and the maximum number of attempts has not been reached.

  • SpecificExceptionRetryPolicy: This policy decides to retry a failed operation based on the type of exception that occurred. It is initialized with a specific exception class, and the shouldRetry method will return true if the exception that caused the failure is an instance of the configured class.

  • TimeoutRetryPolicy: This policy decides to retry a failed operation based on the total elapsed time since the first attempt. It is initialized with a timeout in milliseconds, and the shouldRetry method will return true if the elapsed time since the first attempt is less than the configured timeout.

You can also create your own retry policies by implementing the RetryPolicy interface.

Backoff Policies

A backoff policy determines the delay between retry attempts. RetryMaster includes several built-in backoff policies, such as:

  • ExponentialBackoffPolicy: This policy provides an exponential backoff, meaning that the wait time between retry attempts increases exponentially with each failed attempt. This is a standard error-handling strategy for network applications and helps to gradually reduce the load on the system during a series of failures.

  • ExponentialRandomBackoffPolicy: This policy provides an exponential backoff with a random component. The wait time between retry attempts increases exponentially and, once calculated, an additional random component within a range defined by the calculated interval and the calculated interval multiplied by a multiplier is added. The resulting backoff time is then limited to a maximum interval to prevent the wait time from growing indefinitely. This helps to prevent many instances of an application from all retrying at the same time, potentially overwhelming a system or service, a scenario known as the "thundering herd problem".

  • FixedBackoffPolicy: This policy applies a fixed delay between retry attempts. The wait time is always the same, regardless of the number of attempts. This is useful in situations where the likelihood of a retry succeeding is not related to the number of times it has been tried, and where it's not necessary to increase the delay over time.

  • NoBackoffPolicy: This policy applies no delay between retry attempts. The retries occur immediately after a failure. This is useful in scenarios where you want to retry an operation immediately after a failure without any delay. However, it should be used cautiously as it can potentially lead to higher load on the system in case of persistent failures, due to the absence of any delay between consecutive retry attempts.

  • UniformRandomBackoffPolicy: This policy applies a random delay (within a specified range) between retry attempts. The wait time is a random number uniformly distributed between a minimum and maximum interval. This can be used to introduce a random delay between retries to avoid a thundering herd problem.

You can also create your own backoff policies by implementing the BackoffPolicy interface.

Custom Random Implementation

For the ExponentialRandomBackoffPolicy, UniformRandomBackoffPolicy, and any other policies that utilize a random component, you can specify your own custom random generation logic by creating a class that implements the Random interface. This provides the flexibility to adapt the random behavior to specific requirements of your application or environment.

use IlicMiljan\RetryMaster\Policy\Backoff\ExponentialRandomBackoffPolicy;
use IlicMiljan\RetryMaster\Util\Random;

$randomGenerator = // Your implementation of the Random interface here.

$backoffPolicy = new ExponentialRandomBackoffPolicy();
$backoffPolicy->setRandom($randomGenerator);

The backoff policies in RetryMaster are designed with flexibility in mind and by default they utilize the RandomGenerator implementation provided within the library.

Retry Statistics

The RetryStatistics interface allows you to gather information about retry operations, such as the total number of attempts, the number of successful attempts, the number of failed attempts, and the total sleep time. You can use the provided InMemoryRetryStatistics implementation or create your own.

Retry and Recovery Callbacks

You can define custom logic to execute on each retry attempt and when all retries fail by implementing the RetryCallback and RecoveryCallback interfaces, respectively.

RetryTemplate

The RetryTemplate class simplifies the process of executing operations with retry logic. You provide the operation logic and the RetryTemplate handles the retries according to the configured retry and backoff policies.

Sleeping

The Sleeper interface in RetryMaster is a powerful tool for implementing delay mechanisms in your retry operations. It provides a method to halt the execution of a script for a specified number of milliseconds. It's particularly useful when implementing backoff policies, simulating network latency in testing environments, or throttling requests to a third-party service.

You can replace default NanoSleeper by providing your own implementation in your RetryTemplate:

use IlicMiljan\RetryMaster\RetryTemplateBuilder;
use IlicMiljan\RetryMaster\Util\Sleeper;

$sleeper = // Your implementation of the Sleeper interface here.

$retryTemplate = (new RetryTemplateBuilder())
->setSleeper($sleeper)
->build();

By customizing the Sleeper interface in your RetryTemplate, you have full control over how your application handles delays and sleeping intervals, allowing for precise and effective management of your retry operations.

Logging

RetryMaster comes with an integrated logging system that you can use to monitor and debug your retry operations. It uses the PSR-3 Logger Interface, making it compatible with most logging libraries.

You can set up logging by providing a logger to your RetryTemplate:

use IlicMiljan\RetryMaster\RetryTemplateBuilder;
use Psr\Log\LoggerInterface;

$logger = // Your PSR-3 compatible logger here.

$retryTemplate = (new RetryTemplateBuilder())
                    ->setLogger($logger)
                    ->build();

License

RetryMaster is licensed under the MIT License. This means you can use and modify the code freely as long as you include the original copyright and permission notice in any copy of the software/source.

Credits

RetryMaster is developed and maintained by @IlicMiljan. It's a product of many hours of hard work and dedication, and contributions from the open-source community are greatly appreciated.

This library is greatly inspired by the Spring Retry library, a part of the Spring Framework for Java. The design principles and structure of Spring Retry have been instrumental in shaping RetryMaster. If you're familiar with Spring Retry, you will find many similarities in RetryMaster.

Special thanks to the team behind the Spring Retry library for their impressive work, which serves as a foundation for this project. Their commitment to creating robust and flexible solutions for retry operations has been a significant inspiration.

Finally, a big thank you to all contributors and users of RetryMaster. Your feedback, bug reports, and feature suggestions are invaluable in making this library better. If you would like to contribute, please feel free to submit a pull request.

retry-master's People

Contributors

ilicmiljan avatar

Stargazers

 avatar  avatar

Watchers

 avatar

retry-master's Issues

[BUG] Some Retry Policies Retry One Time Less Than Specified

Describe the bug

When using MaxAttemptsRetryPolicy and SimpleRetryPolicy in the library, the action is retried one time less than specified in constructor parameter.

Here's an example of how this behavior can be reproduced:

  1. Instantiate a MaxAttemptsRetryPolicy or SimpleRetryPolicy with a maximum attempts value of n.
  2. Execute a failing operation wrapped in this retry policy.
  3. Observe that the operation is retried n-1 times, instead of n times as expected.

Expected behavior

The action should be retried n times, where n is the maximum attempts value set in the retry policy.

Environment (please complete the following information):

Actual Output / Stack Trace

There isn't an error message or stack trace for this issue, but the behavior can be observed by counting the number of retries that occur for a failing operation.

Additional context

This issue affects the reliability of operations that rely on MaxAttemptsRetryPolicy and SimpleRetryPolicy for handling retries. It's important to ensure that the number of retries matches the expected value to maintain the integrity of these operations.

[BUG] `ExponentialRandomBackoffPolicy` Does Not Exponentially Increase Backoff Time

Describe the bug

The ExponentialRandomBackoffPolicy in the library does not seem to exponentially increase the backoff time as expected.

Steps to Reproduce

Here's an example of how this behavior can be reproduced:

  1. Instantiate an ExponentialRandomBackoffPolicy.
  2. Execute a failing operation wrapped in this backoff policy.
  3. Monitor the backoff times between retries and observe that they do not increase exponentially.

Expected behavior

The backoff time should increase exponentially between retries when using ExponentialRandomBackoffPolicy.

Environment (please complete the following information):

Actual Output / Stack Trace

There isn't an error message or stack trace for this issue, but the behavior can be observed by monitoring the backoff times between retries.

Additional context

The current behavior of ExponentialRandomBackoffPolicy contradicts the expected functionality of this policy, which should provide an exponential backoff time between retries.

[BUG] `NonRepeatingRetryPolicy` Fails to Retry

Describe the bug

When using the NonRepeatingRetryPolicy in the library, the action does not retry as expected. The last exception stored in the RetryContext is set before checking if the current exception is different than the last, causing the retry mechanism to fail.

Steps to Reproduce

Here's an example of how this behavior can be reproduced:

  1. Instantiate a NonRepeatingRetryPolicy.
  2. Execute a failing operation that throws different exceptions in each attempt.
  3. Observe that the operation does not retry, even though the exceptions are different.

Expected behavior

The action should retry if the current exception is different than the last exception.

Environment (please complete the following information):

Actual Output / Stack Trace

There isn't an error message or stack trace for this issue, but the behavior can be observed by the absence of retries despite different exceptions being thrown.

Additional context

This issue affects operations that rely on NonRepeatingRetryPolicy for handling retries. The current behavior contradicts the expected functionality of this policy, which should allow retries when different exceptions are thrown.

[BUG] `MaxAttemptsRetryPolicy` and `TimeoutRetryPolicy` Lack Default Values

Describe the bug

MaxAttemptsRetryPolicy and TimeoutRetryPolicy in the RetryMaster library do not have default values, unlike the other policies where default values are provided when it makes sense.

Steps to Reproduce

Here's an example of how this behavior can be reproduced:

  1. Try to instantiate MaxAttemptsRetryPolicy or TimeoutRetryPolicy without providing arguments.
  2. An error or unexpected behavior occurs because these classes do not have default values.

Expected behavior

Consistent with other policies in the library, MaxAttemptsRetryPolicy and TimeoutRetryPolicy should have reasonable default values.

Environment (please complete the following information):

Actual Output / Stack Trace

There isn't an error message or stack trace for this issue, but the absence of default values can lead to unexpected behavior or failure to instantiate these classes.

Additional context

The lack of default values for MaxAttemptsRetryPolicy and TimeoutRetryPolicy creates an inconsistency in the library's interface and could lead to user errors.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.