GoogleApisライブラリのExponential Backoffを追いたい
背景
- GoogleApisを利用した実装で、ライブラリがどこまで考慮して実装されているのかを追いたかった
- 例えば、公式ドキュメントによればaccess tokenが期限切れの場合にはrefresh tokenを使用して再取得してくれる、とある
- 同じような仕組みで、リソースへのアクセスが一時的に制限されていた場合の再実行の仕組みが実装されているのか、どのように実装されているのかが気になった
ゴール
- GoogleApisにおいて、再実行の処理がどこでどのように実装されているのか明らかになっていること
事前の情報
このIssueでretryが実装された、とある。
gaxiosで実装されているであろうリトライ処理をデフォルトで有効にするということらしい。
確かに今でもretryはデフォルトでtrueがセットされている
ref. https://github.com/googleapis/nodejs-googleapis-common/blob/v7.1.0/src/apirequest.ts#L316
ということでgaxiosから見ていく
gaxios上の実装を追う
おそらくエンドポイントとして利用されるのは request
メソッドだと思われる。
ref. https://github.com/googleapis/gaxios/blob/v6.4.0/src/gaxios.ts#L133
_request
で実際にリクエストを投げているのは上から渡ってきているopts
で指定されたadapter
にあると思われる。
ただ、ここを追わんでもこのロジックのtry-catchでそれっぽいことをやっているように見える。
ref. https://github.com/googleapis/gaxios/blob/v6.4.0/src/gaxios.ts#L183-L201
以下内容を抽出
const err =
e instanceof GaxiosError
? e
: new GaxiosError((e as Error).message, opts, undefined, e as Error);
const {shouldRetry, config} = await getRetryConfig(err);
if (shouldRetry && config) {
err.config.retryConfig!.currentRetryAttempt =
config.retryConfig!.currentRetryAttempt;
// The error's config could be redacted - therefore we only want to
// copy the retry state over to the existing config
opts.retryConfig = err.config?.retryConfig;
return this._request<T>(opts);
}
shouldRetry
だったらもっかい同じメソッドを叩いてくれていそう。
ただ、これだけみるとexponential backoffの実装に見えない。
retryConfig
がPromiseを返してくるのが怪しいので覗いてみる。
getRetryConfig
出発点はここ
ref. https://github.com/googleapis/gaxios/blob/v6.4.0/src/retry.ts#L16
雑多なメモ的な残し
- 指定がなければretryを3にセットしている
- retryするのは429と5xx系っぽい
- shouldRetry関数を上書きするとコントロールできるっぽい
- https://github.com/googleapis/gaxios/blob/v6.4.0/src/retry.ts#L57
- オプション自体はinsertの第二引数に渡せるoptionの規定クラスであるGaxiosOptionsにある
ここらへん以降にexponential backoff的な実装がある
あとはコメントに書いてある通りではあるが、retryのbackoffを計算してその分setTimeoutで待っている。
exponential backoffの実装だけ抜き出す
多分こんな感じで計算自体はされてるはず
const retryDelay = (attempt: number = 0): number => {
return (attempt ?? 100) + ((Math.pow(2, attempt) - 1) / 2) * 1000
}
気になったこと
exponential backoffってある程度の揺らぎが必要なんじゃなかったっけ。
この記事のイメージ。
ref. https://cloud.google.com/memorystore/docs/redis/exponential-backoff?hl=ja
ゆらぎが必要な理由
AWSのドキュメントだけど、ここで言及されているジッターを入れてるだけか。
過負荷または競合が障害の原因である場合、バックオフが非常に役に立たないことがよくあります。これは相関関係が原因です。失敗したすべてのコールが同じ時間にバックオフされた場合、再試行時に再び競合または過負荷が発生します。私たちのソリューションはジッターです。ジッターはバックオフにある程度のランダム性を追加して、再試行を時間内に分散させます。
ref. https://aws.amazon.com/jp/builders-library/timeouts-retries-and-backoff-with-jitter/