Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ravichaturvedi/retrier
Java8 library to simplify retries
https://github.com/ravichaturvedi/retrier
cloud distributed-systems java-8 lambda microservices resilent
Last synced: 1 day ago
JSON representation
Java8 library to simplify retries
- Host: GitHub
- URL: https://github.com/ravichaturvedi/retrier
- Owner: ravichaturvedi
- License: apache-2.0
- Created: 2017-07-02T18:02:13.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-04-13T03:20:14.000Z (over 6 years ago)
- Last Synced: 2024-10-13T15:41:30.733Z (about 1 month ago)
- Topics: cloud, distributed-systems, java-8, lambda, microservices, resilent
- Language: Java
- Homepage: https://ravichaturvedi.github.io/retrier
- Size: 96.7 KB
- Stars: 1
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Retrier
[![Build Status](https://travis-ci.org/ravichaturvedi/retrier.svg?branch=master)](https://travis-ci.org/ravichaturvedi/retrier)
Java library to simplify retries of any function or code block (piece of code wrapped in lambda expression) without
changing the execution flow from developer perspective. They can rely on the return value of the function/code block in retry, etc..## Getting Started
Add the following `maven` dependency to your project `pom.xml`:
```xml
io.github.ravichaturvedi
retrier
0.2```
```java
import static io.github.ravichaturvedi.retrier.Retry.*;// Retrying on some piece of code which may return value or may throw exception.
// Result will be of same type as returned by the lambda expression.
Map result = retry(() -> {
// some operation which may throw exception
return new HashMap();
});// Retrying on some piece of code which doesn't return any value but can throw exception
retry(() -> {
// some operation which may throw exception
});
```* `Retry.retry` is using the default `Retrier` with Retry Count `3`, Exponential Backoff initial Delay of `1 second` and retry timeout duration of `15 second`.
## Background
Generally, we call an external system/service using some RPC mechanism (REST/GRPC/etc..) but we immediately inform about the failure to the client/user.
But in case if it would have been some transient failure (network issue/connectivity issue/etc..) then we could have retried and get success next time.
That would be a much nice experience to the client/user calling your service.These kind of situation occurs in distributed system, microservice, mobile application and even within the same process space if some method/code block is throwing exception transiently.
There are many strategies to deal with retries and generally due to the induced complexity in the code, developer tends not to do it in while writing the first version of the program/software.
This library fills that gap by providing a library, which in turn provides an elegant abstraction to deal with retries of any function/code-block.The whole idea behind building thins library is to encourage developers to build more resilient systems.
## Features
* Retry on any function/code-block which returns value (of any type!) and may throw RuntimeException not declared as method signature.
* Retry on any function/code-block which does not returns any value and may throw RuntimeException not declared as method signature.
* Retries can capture the context by using lambda expression so function can have any number of arguments, passed in by the captured context in lambda expression.
* Retry upto number of times.
* Retry upto the timeout expires.
* Retry with exponential backoff delay.
* Retry with exponential backoff with max backoff delay.
* Retry on any Exception type so if the provided exception type `isAssignableFrom` the thrown exception then retry will happen.
* Retry but execute some piece of code when specific exception occurs (check is based on `isAssignableFrom`), like populate the data etc.
* Retry on nested exception wrapped within the other exception (as returned by getCause() method).
* Retry can have constraint from all the above permutation/combination.
* Retry returns back the exact same exception as thrown inside the retry block.## Drawbacks
* Returned exception will be captured into `Exception` type, so user need to handle that if required.
But as the actual exception gets propogated so should be easily taken care of.## Safety
Keep in mind any side effects that may occur if the operation is executed multiple times.
In general, the it is safe to retry only [idempotent](https://en.wikipedia.org/wiki/Idempotence) operations.
Idempotent operations are those which do not change the result when applied repeatedly.Examples of `idempotent` operation:
* Put a value in some store corresponding to a key without any condition.
* Read operations that have no side effect
* HTTP GET or PUT (typically)Examples of `non-idempotent` operation:
* Increment a counter
* HTTP POST (typically)## Usages
Specifying below some typical retry use cases covered by the Retrier.Assuming there are methods named `foo` and `bar` that throws `Exception` and following imports are in place:
```java
import static io.github.ravichaturvedi.retrier.Retriers.*;
import static io.github.ravichaturvedi.retrier.Retry.*;void foo() throws Exception;
T bar(T) throws Exception;
```1. Retry number of times
```java
Retrier retrier = create(withRetryCount(3));// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns integer.
int result = retrier.retry(on(Exception.class), () -> bar(2));
```2. Retry number of times until timeout expires
```java
Retrier retrier = create(withRetryCount(3),
withTimeout(Duration.of(15, ChronoUnit.SECONDS)));
// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns Object.
Object result = retrier.retry(on(Exception.class), () -> bar(new Object()));
```3. Retry unlimited number of times until timeout expires.
```java
Retrier retrier = create(withTimeout(Duration.of(15, ChronoUnit.SECONDS)));// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns a map.
Map result = retrier.retry(on(Exception.class), () -> {
return new HashMap();
});
```4. Retry unlimited number of times with exponential backoff delay.
```java
Retrier retrier = create(withExpBackoff(Duration.of(3, ChronoUnit.SECONDS))));// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns a long.
long result = retrier.retry(on(Exception.class), () -> {
return 2L;
});
```5. Retry unlimited number of times with exponential backoff delay and max backoff delay.
```java
Retrier retrier = create(withExpBackoff(Duration.of(3, ChronoUnit.SECONDS), Duration.of(9, ChronoUnit.SECONDS)));// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns a long.
long result = retrier.retry(on(Exception.class), () -> {
return 2L;
});
```6. Retry unlimited number of times with exponential backoff delay and max backoff delay but on nested exception.
```java
Retrier retrier = create(withExpBackoff(Duration.of(3, ChronoUnit.SECONDS), Duration.of(9, ChronoUnit.SECONDS)));// Retring in lambda that doesn't return anything
retrier.retry(onNested(IllegalArgumentException.class), () -> foo());// Retrying on lambda that returns a long.
long result = retrier.retry(onNested(IllegalArgumentException.class), () -> {
return 2L;
});
```7. Retry number of times with timeout and exponential backoff with initial delay.
```java
Retrier retrier = create(withRetryCount(3),
withTimeout(Duration.of(15, ChronoUnit.SECONDS)),
withExpBackoff(Duration.of(3, ChronoUnit.SECONDS)));
// Retring in lambda that doesn't return anything
retrier.retry(on(Exception.class), () -> foo());// Retrying on lambda that returns a long.
long result = retrier.retry(on(Exception.class), () -> bar(2L));
```8. Retry number of times with timeout, exponential backoff with initial delay and max backoff delay.
```java
Retrier retrier = create(withRetryCount(3),
withTimeout(Duration.of(15, ChronoUnit.SECONDS)),
withExpBackoff(Duration.of(3, ChronoUnit.SECONDS), Duration.of(9, ChronoUnit.SECONDS)));
retrier.retry(on(Exception.class), () -> foo());
Map result = retrier.retry(on(Exception.class), () -> bar(new HashMap()));
```9. Retry number of times with timeout, exponential backoff with initial delay, max backoff delay and trace to see how retrier itself is working.
Since withTrace accepts any lambda which takes `java.lang.String` as only parameter, so can be hooked with any type of logging infrastructure.
```java
Retrier retrier = create(withRetryCount(3),
withTimeout(Duration.of(15, ChronoUnit.SECONDS)),
withExpBackoff(Duration.of(3, ChronoUnit.SECONDS), Duration.of(9, ChronoUnit.SECONDS)),
withTrace(System.out::println));
retrier.retry(on(Exception.class), () -> foo());
Set result = retrier.retry(on(Exception.class), () -> bar(new HashSet()));
```10. Retry number of times with timeout, exponential backoff with initial delay, max backoff delay and trace to see how retrier itself is working.
Also if specific exception occurs then execute some piece of code.Since withTrace accepts any lambda which takes `java.lang.String` as only parameter, so can be hooked with any type of logging infrastructure.
```java
Retrier retrier = create(withRetryCount(3),
withTimeout(Duration.of(15, ChronoUnit.SECONDS)),
withExpBackoff(Duration.of(3, ChronoUnit.SECONDS), Duration.of(9, ChronoUnit.SECONDS)),
withTrace(System.out::println));
retrier.retry(() -> {
foo();
}, on(on(IllegalStateException.class), on(IllegalArgumentException.class, ()-> {
// Some handling of exception
})));retrier.retry(() -> {
return bar();
}, on(on(IllegalStateException.class), on(IllegalArgumentException.class, ()-> {
// Some handling of exception
})));
```## Credits
* This library is inspired by my previous experience in dealing with microservices and handling failures.