• Stars
    star
    301
  • Rank 138,451 (Top 3 %)
  • Language
    Kotlin
  • License
    ISC License
  • Created about 5 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A higher-order function for retrying operations that may fail.

kotlin-retry

Maven Central CI Status License

retry is a higher-order function for retrying operations that may fail.

retry(limitAttempts(10) + constantDelay(delayMillis = 50L)) {
    /* your code */
}

Installation

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.michael-bull.kotlin-retry:kotlin-retry:1.0.9")
}

Introduction

IO operations often experience temporary failures that warrant re-execution, e.g. a database transaction that may fail due to a deadlock.12

“even if your application logic is correct, you must still handle the case where a transaction must be retried”

Deadlocks in InnoDB

The retry function simplifies this process by wrapping the application logic and applying a specified RetryPolicy.

In the example below, either of the calls to customers.nameFromId may fail, abandoning the remaining logic within the printExchangeBetween function. As such, we may want to retry this operation until 5 attempts in total have been executed:

import com.github.michaelbull.retry.policy.limitAttempts
import com.github.michaelbull.retry.retry
import kotlinx.coroutines.runBlocking

suspend fun printExchangeBetween(a: Long, b: Long) {
    val customer1 = customers.nameFromId(a)
    val customer2 = customers.nameFromId(b)
    println("$customer1 exchanged with $customer2")
}

fun main() = runBlocking {
    retry(limitAttempts(5)) {
        printExchangeBetween(1L, 2L)
    }
}

We can also provide a RetryPolicy that only retries failures of a specific type. The example below will retry the operation only if the reason for failure was a SQLDataException, pausing for 20 milliseconds before retrying and stopping after 5 total attempts.

import com.github.michaelbull.retry.ContinueRetrying
import com.github.michaelbull.retry.StopRetrying
import com.github.michaelbull.retry.policy.RetryPolicy
import com.github.michaelbull.retry.policy.constantDelay
import com.github.michaelbull.retry.policy.limitAttempts
import com.github.michaelbull.retry.policy.plus
import com.github.michaelbull.retry.retry
import kotlinx.coroutines.runBlocking
import java.sql.SQLDataException

val retryTimeouts: RetryPolicy<Throwable> = {
    if (reason is SQLDataException) ContinueRetrying else StopRetrying
}

suspend fun printExchangeBetween(a: Long, b: Long) {
    val customer1 = customers.nameFromId(a)
    val customer2 = customers.nameFromId(b)
    println("$customer1 exchanged with $customer2")
}

fun main() = runBlocking {
    retry(retryTimeouts + limitAttempts(5) + constantDelay(20)) {
        printExchangeBetween(1L, 2L)
    }
}

Backoff

The examples above retry executions immediately after they fail, however we may wish to spread out retries with an ever-increasing delay. This is known as a "backoff" and comes in many forms. This library includes all the forms of backoff strategy detailed the article by Marc Brooker on the AWS Architecture Blog entitled "Exponential Backoff And Jitter".

Binary Exponential

“exponential backoff means that clients multiply their backoff by a constant after each attempt, up to some maximum value”

sleep = min(cap, base * 2 ** attempt)
retry(limitAttempts(5) + binaryExponentialBackoff(base = 10L, max = 5000L)) {
    /* code */
}

Full Jitter

“trying to improve the performance of a system by adding randomness ... we want to spread out the spikes to an approximately constant rate”

sleep = random_between(0, min(cap, base * 2 ** attempt))
retry(limitAttempts(5) + fullJitterBackoff(base = 10L, max = 5000L)) {
    /* code */
}

Equal Jitter

“Equal Jitter, where we always keep some of the backoff and jitter by a smaller amount”

temp = min(cap, base * 2 ** attempt)
sleep = temp / 2 + random_between(0, temp / 2)
retry(limitAttempts(5) + equalJitterBackoff(base = 10L, max = 5000L)) {
    /* code */
}

Decorrelated Jitter

“Decorrelated Jitter, which is similar to “Full Jitter”, but we also increase the maximum jitter based on the last random value”

sleep = min(cap, random_between(base, sleep * 3))
retry(limitAttempts(5) + decorrelatedJitterBackoff(base = 10L, max = 5000L)) {
    /* code */
}

Inspiration

Contributing

Bug reports and pull requests are welcome on GitHub.

License

This project is available under the terms of the ISC license. See the LICENSE file for the copyright information and licensing terms.

More Repositories

1

kotlin-result

A multiplatform Result monad for modelling success or failure operations.
Kotlin
866
star
2

kotlin-inline-logger

A logger facilitating lazily-evaluated log calls via Kotlin's inline classes & functions.
Kotlin
83
star
3

aurelia-hacker-news

A recreation of the Hacker News website written in TypeScript and built with Aurelia.
TypeScript
60
star
4

zoom.ts

A lightweight TypeScript library for image zooming, as seen on Medium.
TypeScript
48
star
5

material-bottom-nav

A bottom navigation bar adhering to the Material Design specification.
SCSS
46
star
6

kotlin-coroutines-jdbc

A library for interacting with blocking JDBC drivers using Kotlin Coroutines.
Kotlin
41
star
7

rs-api

An open-source implementation of a web-service client, written in Java, that allows interaction with the various APIs available for the popular MMORPG; RuneScape.
Java
33
star
8

vlc-credit-skipper

Automatically skip intro/outro credit sequences in VLC.
Lua
31
star
9

aurelia-typescript-webpack-starter

A minimal Aurelia starter kit written in TypeScript and built using webpack.
TypeScript
27
star
10

c-dictionary-trie

A dictionary implementation that is written in C and backed by a prefix tree.
C
24
star
11

loona-intro-generator

Create your own animated intro sequence based on the LOONA music videos.
SCSS
19
star
12

beancount-plugins

A collection of my custom beancount importers & price sources, written in Python
Python
15
star
13

aurelia-swipeout

A custom element for iOS style swipeout actions, backed by Hammer.js
TypeScript
13
star
14

aurelia-split-pane

A custom element for resizable split panes.
TypeScript
12
star
15

spring-boot-starter-recaptcha

Spring Boot starter for Google's reCAPTCHA v3.
Kotlin
8
star
16

svg-stockpile

An optimizing and stacking tool for Scalable Vector Graphics, written in Java.
Java
6
star
17

ModBot

Automated moderation for VBulletin based forums using the VBulletin Mobile API.
Java
5
star
18

kotlin-quadtree

A quadtree implemented in Kotlin.
Kotlin
5
star
19

wysiwyg-editor

A “what-you-see-is-what-you-get” editor built on React & Slate.
JavaScript
5
star
20

advent-2021

Solutions for Advent of Code 2021, written in Kotlin.
Kotlin
4
star
21

spring-boot-deployment-demo

Java
4
star
22

react-gsi

React bindings for the 'Sign in With Google for Web' API
TypeScript
4
star
23

spring-boot-sitemap-demo

Java
3
star
24

aurelia-ssr-starter

A minimal Aurelia starter kit supporting server side rendering.
TypeScript
3
star
25

advent-2019

Solutions for Advent of Code 2019
Kotlin
2
star
26

karma-fail-fast-reporter

A Karma plugin. Report failures as soon as they occur.
JavaScript
2
star
27

m2m-sms

M2M to SMS is a website platform written in PHP that facilitates the transit of Machine-to-Machine device messages, formatted as Short Message Service messages, over the Simple Object Access Protocol via the EE M2M Connect Service.
PHP
2
star
28

advent-2023

Solutions for Advent of Code 2023, written in Kotlin.
Kotlin
1
star