backoff

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2025 License: MIT Imports: 0 Imported by: 0

README

This is a minimalist exponential backoff implementation for Go. It focuses on absolute simplicity and only contains two functions, of which you may not even need the second one.

It provides high performance and does not use floating-point arithmetic.

See https://pkg.go.dev/github.com/codesoap/backoff for the full documentation.

Usage

Here is a simple example; more can be found in the documentation:

package main

import (
	"fmt"
	"github.com/codesoap/backoff"
)

func myActionThatMightFail() bool {
	// Emulate an action that always fails for demonstration purposes:
	return false
}

func main() {
	limiter := backoff.FailLimiter{}
	for i := 1; i <= 12; i++ {
		tried := limiter.Try(myActionThatMightFail)
		if tried {
			fmt.Printf("#%02d: Executed action.\n", i)
		} else {
			fmt.Printf("#%02d: Skipped action.\n", i)
		}
	}
	// Output:
	// #01: Executed action.
	// #02: Skipped action.
	// #03: Executed action.
	// #04: Skipped action.
	// #05: Skipped action.
	// #06: Executed action.
	// #07: Skipped action.
	// #08: Skipped action.
	// #09: Skipped action.
	// #10: Skipped action.
	// #11: Executed action.
	// #12: Skipped action.
}

Documentation

Overview

Package backoff provides a simple exponential backoff implementation.

Example (CappedFailProgression)
package main

import (
	"fmt"

	"github.com/codesoap/backoff"
)

func main() {
	myActionThatMightFail := func() bool {
		// Emulate an action that always fails for demonstration purposes:
		return false
	}

	limiter := backoff.FailLimiter{SkipLimit: 2}
	for i := 1; i <= 12; i++ {
		tried := limiter.Try(myActionThatMightFail)
		if tried {
			fmt.Printf("#%02d: Executed action.\n", i)
		} else {
			fmt.Printf("#%02d: Skipped action.\n", i)
		}
	}
}
Output:

#01: Executed action.
#02: Skipped action.
#03: Executed action.
#04: Skipped action.
#05: Skipped action.
#06: Executed action.
#07: Skipped action.
#08: Skipped action.
#09: Executed action.
#10: Skipped action.
#11: Skipped action.
#12: Executed action.
Example (FailProgression)
package main

import (
	"fmt"

	"github.com/codesoap/backoff"
)

func main() {
	myActionThatMightFail := func() bool {
		// Emulate an action that always fails for demonstration purposes:
		return false
	}

	limiter := backoff.FailLimiter{}
	for i := 1; i <= 12; i++ {
		tried := limiter.Try(myActionThatMightFail)
		if tried {
			fmt.Printf("#%02d: Executed action.\n", i)
		} else {
			fmt.Printf("#%02d: Skipped action.\n", i)
		}
	}
}
Output:

#01: Executed action.
#02: Skipped action.
#03: Executed action.
#04: Skipped action.
#05: Skipped action.
#06: Executed action.
#07: Skipped action.
#08: Skipped action.
#09: Skipped action.
#10: Skipped action.
#11: Executed action.
#12: Skipped action.
Example (SlowFailProgression)
package main

import (
	"fmt"

	"github.com/codesoap/backoff"
)

func main() {
	myActionThatMightFail := func() bool {
		// Emulate an action that always fails for demonstration purposes:
		return false
	}

	limiter := backoff.FailLimiter{BackoffInterval: 2}
	for i := 1; i <= 12; i++ {
		tried := limiter.Try(myActionThatMightFail)
		if tried {
			fmt.Printf("#%02d: Executed action.\n", i)
		} else {
			fmt.Printf("#%02d: Skipped action.\n", i)
		}
	}
}
Output:

#01: Executed action.
#02: Skipped action.
#03: Executed action.
#04: Skipped action.
#05: Executed action.
#06: Skipped action.
#07: Skipped action.
#08: Executed action.
#09: Skipped action.
#10: Skipped action.
#11: Executed action.
#12: Skipped action.
Example (WithError)
package main

import (
	"fmt"

	"github.com/codesoap/backoff"
)

func main() {
	// Emulate a simple action function for demonstration purposes:
	myActionThatMightFail := func(someParameter int) error {
		if someParameter > 3 {
			return fmt.Errorf("I don't like large numbers")
		}
		return nil
	}

	limiter := backoff.FailLimiter{}
	for i := 1; i <= 12; i++ {
		// If the tried action requires parameters or returns an error or
		// other values, which are needed for further steps, they can be
		// passed out using closures:
		var err error
		tried := limiter.Try(func() bool {
			err = myActionThatMightFail(i)
			return err == nil
		})
		if tried {
			if err != nil {
				fmt.Printf("#%02d: Executed action, but failed: %v\n", i, err)
				continue
			}
			fmt.Printf("#%02d: Executed action and succeeded.\n", i)
			// Do more computations after success...
		} else {
			fmt.Printf("#%02d: Skipped action.\n", i)
		}
	}
}
Output:

#01: Executed action and succeeded.
#02: Executed action and succeeded.
#03: Executed action and succeeded.
#04: Executed action, but failed: I don't like large numbers
#05: Skipped action.
#06: Executed action, but failed: I don't like large numbers
#07: Skipped action.
#08: Skipped action.
#09: Executed action, but failed: I don't like large numbers
#10: Skipped action.
#11: Skipped action.
#12: Skipped action.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FailLimiter added in v0.2.0

type FailLimiter struct {
	// BackoffInterval determines how many times an action has to fail in a
	// row before the amount of skips between attempts is doubled.
	//
	// If BackoffInterval is 0 or smaller, it will be treated as being 1.
	BackoffInterval int

	// SkipLimit defines the maximum amount of times an action is
	// skipped before retrying it. It will be ignored if it is 0.
	SkipLimit int
	// contains filtered or unexported fields
}

A FailLimiter limits the amount of calls to a function, if the function fails. Controlled by its BackoffInterval and SkipLimit fields, the amount of calls to the function become increasingly rare, if the function continues to fail.

func (*FailLimiter) Reset added in v0.2.0

func (fl *FailLimiter) Reset()

Reset resets any progression in the limits and ensures that the passed action is not skipped at the next call of fl.Try.

func (*FailLimiter) Try added in v0.2.0

func (fl *FailLimiter) Try(action func() bool) bool

Try calls action if no limit is currently active. If action returns true, any progression in the limit will be reset. If action returns false, the next call(s) of Try will not call action. If action continuously fails, the calls of action will become increasingly rare according to fl.BackoffInterval and fl.SkipLimit.

Try returns true, if action has been called and false, if a limit prevented the call.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL