♣️

Go言語でエクスポネンシャルバックオフ処理を実装する パート2

2024/03/13に公開

前回の記事ではエクスポネンシャルバックオフを自前で実装しましたが、今回はcenkalti/backoffというライブラリを使って実装してみたいと思います。

実際のコード

// メインの処理
func doOperation() error{
    fmt.Printf("doOperation ")
    // わざとエラーで返す
    return errors.New("error")
}

func doNotify(err error, duration time.Duration) {
    fmt.Printf("エラー: %v %d秒後に再試行します...\n", err, duration/time.Second)
}

func main() {
    operation := backoff.Operation(doOperation)
    notify := backoff.Notify(doNotify)
    
    exponentialBackOff := backoff.NewExponentialBackOff()
    // リトライの初期間隔
    exponentialBackOff.InitialInterval = 1 * time.Second
    // リトライ間隔を決めるランダム値
    exponentialBackOff.RandomizationFactor = 0
    // リトライ間隔を決める乗数
    exponentialBackOff.Multiplier = 2
    
    err :=backoff.RetryNotify(operation,backoff.WithMaxRetries(exponentialBackOff,3),notify)
    if err != nil {
        fmt.Println("処理失敗")
    } else {
        fmt.Println("処理成功")
    }
}

実行結果

doOperation
エラー: error 1秒後に再試行します...
doOperation
エラー: error 2秒後に再試行します...
doOperation
エラー: error 4秒後に再試行します...
doOperation
処理失敗

コードの説明

リトライ間隔は RetryInterval * (範囲 [1 - RandomizationFactor, 1 + RandomizationFactor] 内のランダム値) の式を元に算出されており、今回の場合だと

InitialInterval(RetryInterval) = 1秒
RandomizationFactor = 0
Multiplier = 2

と指定しており、1秒、2秒、4秒、8秒…ごとの間隔でリトライを実行します。

backoff.RetryNotify(operation,backoff.WithMaxRetries(exponentialBackOff,3),notify)
では WithMaxRetries の第2引数でリトライ回数を指定しています。
今回は3回リトライを実行するように指定しています。

エラーによってリトライするかどうか判定する

エラーによってはリトライさせずに処理を終了させたい場合があると思います。
backoff.Permanent()を使うことで任意のエラーの時はリトライせずに終了させることができます。

func doOperation() error {
    // 0〜1の乱数を生成
    randInt := rand.Intn(2)
    if randInt == 0 {
        return errors.New("error need retry")
    }
    // randIntが0以外の時はリトライせずに処理を終了する
    return backoff.Permanent(errors.New("error permanent"))
}

func doNotify(err error, duration time.Duration) {
    fmt.Printf("エラー: %v %d秒後に再試行します...\n", err, duration/time.Second)
}

func main() {
    operation := backoff.Operation(doOperation)
    notify := backoff.Notify(doNotify)
    exponentialBackOff := backoff.NewExponentialBackOff()
    exponentialBackOff.InitialInterval = 1 * time.Second
    exponentialBackOff.RandomizationFactor = 0
    exponentialBackOff.Multiplier = 2
    err := backoff.RetryNotify(operation,backoff.WithMaxRetries(exponentialBackOff, 3), notify)
    if err != nil {
        fmt.Printf("処理失敗 %v \n", err)
    } else {
        fmt.Println("処理成功")
    }
}

実行結果

エラー: error need retry 1秒後に再試行します...
エラー: error need retry 2秒後に再試行します...
処理失敗 error permanent 

まとめ

cenkalti/backoffというライブラリを使ってエクスポネンシャルバックオフを実装してみました。
簡単に実装できるので、手軽にリトライ処理を実装したいと思った場合はこのライブラリを使ってみるといいかもしれません。

レスキューナウテックブログ

Discussion