Backend Development 12 min read

Design and Usage of a Lightweight Go Retry Library

This article introduces a lightweight Go library named Retry that provides functional‑programming‑style retry mechanisms, explains the pain points of manual loop retries, describes its architecture, backoff algorithms, configuration options, and demonstrates both singleton and factory usage with complete code examples.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Design and Usage of a Lightweight Go Retry Library

In daily development, developers often write repetitive loop‑based retry logic for remote service calls, which quickly becomes hard to maintain. The author, a veteran with 17 years in IT, presents a Go library called Retry that abstracts this pattern into a clean, functional‑programming interface.

The library addresses several pain points: duplicated for loops, fixed retry counts and intervals, low reusability, and difficulty handling non‑error return types. It offers a unified API to configure retry attempts, backoff strategies, jitter, and custom retry conditions.

Architecture : Retry is built around a configurable Config struct and supports two usage modes—singleton and factory. The singleton mode exposes simple functions Do and DoWithDefault , while the factory mode creates a Retry object via New and executes functions with TryOnConflict .

Backoff module : The library implements three backoff strategies— FixBackOff (fixed delay), RandomBackOff (random delay), and ExponentialBackOff (exponential delay). These can be combined using CombineBackOffs to produce flexible retry intervals. The delay calculation follows the formula: backoff = backoffFunc(factor * count + jitter * rand.Float64()) * 100 * Millisecond + delay where factor , jitter , count , and delay are configurable via WithFactor , WithJitter , WithInitDelay , etc.

Configuration options include WithCallback , WithContext , WithAttempts , WithAttemptsByError , WithFactor , WithInitDelay , WithJitter , WithRetryIfFunc , WithBackOffFunc , and WithDetail . These allow fine‑grained control over retry behavior and error handling.

Result handling : After execution, a Result struct is returned, containing fields such as count , data , tryError , and execErrors . Helper methods like Count() , Data() , TryError() , ExecErrors() , LastExecError() , and IsSuccess() simplify result inspection.

Code examples :

Singleton mode example:

package main

import (
    "fmt"
    "github.com/shengyanli1982/retry"
)

// retryable function
func testFunc() (any, error) {
    return "lee", nil
}

func main() {
    // retry call
    result := retry.DoWithDefault(testFunc)

    // result
    fmt.Println("result:", result.Data())
    fmt.Println("tryError:", result.TryError())
    fmt.Println("execErrors:", result.ExecErrors())
    fmt.Println("isSuccess:", result.IsSuccess())
}

Factory mode example:

package main

import (
    "errors"
    "fmt"
    "github.com/shengyanli1982/retry"
)

// retryable functions
func testFunc1() (any, error) {
    return "testFunc1", nil
}

func testFunc2() (any, error) {
    return nil, errors.New("testFunc2")
}

func main() {
    // retry with config
    r := retry.New(nil)

    // try on conflict for testFunc1
    result := r.TryOnConflict(testFunc1)
    fmt.Println("========= testFunc1 =========")
    fmt.Println("result:", result.Data())
    fmt.Println("tryError:", result.TryError())
    fmt.Println("execErrors:", result.ExecErrors())
    fmt.Println("isSuccess:", result.IsSuccess())

    // try on conflict for testFunc2
    result = r.TryOnConflict(testFunc2)
    fmt.Println("========= testFunc2 =========")
    fmt.Println("result:", result.Data())
    fmt.Println("tryError:", result.TryError())
    fmt.Println("execErrors:", result.ExecErrors())
    fmt.Println("isSuccess:", result.IsSuccess())
}

Both examples show how the library abstracts retry logic, reduces boilerplate, and provides detailed error information when needed.

Conclusion : Retry is a lightweight, easy‑to‑use Go library that standardizes retry operations through functional programming patterns. Its dual modes, flexible backoff strategies, and comprehensive configuration make it suitable for most backend retry scenarios, improving code readability and maintainability.

BackendGoretryFunctional Programmingerror handlinglibrarybackoff
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.