🐣

Go で処理を途中からリトライできるパッケージを作った

2 min read

なぜ作ったのか

通信処理やインストール処理など、一連の処理を Step by step に実行してステップ毎にリトライさせることがあったので、その実装をパッケージとして切り出してみました。

https://github.com/kenkyu392/go-retry

最初はとりあえずコンテキストがキャンセルされるまでリトライするだけの実装でしたが、さすがに現在は Exponential Backoff や最大リトライ回数など最低限必要なものを実装しています。

使い方

使い方は retry.DoWithContext にリトライ間隔を設定する retry.DurationFunc を設定し、あとは実行したい処理をステップ毎に関数化して渡すだけです。

package main

import (
	"context"
	"log"

	"github.com/kenkyu392/go-retry"
)

func main() {
	// キャンセル用にコンテキストを用意する。
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 処理を実行する。
	errs := retry.DoWithContext(ctx,
		// Exponential Backoff を行いながら 5 回リトライする。
		retry.ExponentialBackoff(5),
		// 実行したい処理をステップ毎に関数に分離して渡す。
		func(ctx context.Context) error {
			// リトライ不可能な何かが発生した場合は
			// retry.Canceled で処理をキャンセルする。
			if failed {
				return retry.Canceled
			}
			return nil
		},
		// 前のステップが成功したら次のステップに進む。
		func(ctx context.Context) error {
			return nil
		},
	)

	// 実行中に発生したエラーを全て確認する。
	for _, err := range errs {
		log.Print(err)
	}
}

Exponential Backoff とは?

簡単に説明すると、処理の失敗回数に合わせてリトライ間隔を指数関数的に長くするアルゴリズムです。リトライ間隔を長くすることの必要性などは詳しく調べると良い説明が出てくると思いますが、用途としては「処理の衝突を避けるため」や「負荷を軽減するため」に実装することが多いと思います。

https://cloud.google.com/iot/docs/how-tos/exponential-backoff

https://docs.aws.amazon.com/general/latest/gr/api-retries.html

リトライ間隔の調整

リトライ間隔の調整には retry.DurationFunc を使用します。現在このパッケージには標準的な retry.ExponentialBackoffretry.Duration が実装されています。

リトライ間隔の調整を関数にした理由は、単純にカスタムできる実装にするのが好きなこともありますが、Exponential Backoff には様々な実装があることが大きいです。例えば、現在実装されている retry.ExponentialBackoff200ms + (20ms ~ 100ms) のようにランダムな値を使用したアルゴリズムで実装されていますが、ランダムな値を使用しない Exponential Backoff を使用したい場合は、以下のような独自関数を実装することで簡単に使用できます。

func ExponentialBackoff(maxRetries int) retry.DurationFunc {
	return func(retries int) time.Duration {
		if maxRetries >= 0 && maxRetries < retries {
			return -1
		}
		return time.Duration(math.Pow(2, float64(retries))*100)*time.Millisecond
	}
}

まとめ

このパッケージは通信処理やインストール処理、データベースなどの他に対話型のインターフェースを伴う CLI などで使用できるように設計して実装しています。いろいろな場所に組み込みやすい設計にしているので、良ければ使ってみてください。
retry.DurationFunc の追加やご指摘などコントリビュートもお待ちしてます。

https://github.com/kenkyu392/go-retry

Discussion

ログインするとコメントできます