Posted at

RxJSのretryWhen Operatorで複数回の遅延リトライ


はじめに

次のようなユースケースを想定してタイトルの通り RxJSのretryWhen Operatorsで複数回の遅延リトライ を試してみたいと思います。


  • rateLimitがあるWebAPIの呼び出し

  • AWS SDKなどの呼び出し時にリトライ処理に遅延時間を設定したい場合


    • 今回この記事を書こうと思ったきっかけも AWS Cognito SDKのrateLimit制限を避けるためでした...




たまに失敗して、連続で呼ぶとrateLimitで弾かれるソースを作成

リトライ処理の例を示す前に、リトライ対象となるモックのソースを作成します。

rateLimitがあるWebAPIをイメージするとわかりやすいかと思います。

以下の例では、次のようなエラーが発生するようにしています。


  • たまに andom error が発生

  • 1sの間に5回以上の呼び出して、ate limit error が発生

const rateLimitPerSec = 5;

let rateHistory: number[] = [];

// mock source
const source$ = () =>
of(Math.floor(Math.random() * 5)).pipe(
mergeMap((value) => {
rateHistory = [Date.now(), ...rateHistory];
rateHistory.length = rateLimitPerSec;

const callTimeRateLimitAgo = +rateHistory[rateLimitPerSec - 1];

if (!Number.isNaN(callTimeRateLimitAgo)) {
const sec = 1 * 1000;
if (Date.now() - callTimeRateLimitAgo <= sec) {
return throwError('rate limit error occur');
}
}

return of(value);
}),
mergeMap((value) => {
if (value === 0) {
return throwError('random error occur');
}

return of(value);
}),
);

※ ちょっと雑なところはありますが、雰囲気が伝わればいいかと


普通にretry Operatorでn回のリトライをする

上で作成したmockのソースに リトライ回数を指定して retry Operator を挟む

const retryCount = 5;

// retryを使用
source$()
.pipe(
retry(retryCount),
)
.subscribe((value) => console.log(value), (e) => console.log(e));


retryWhen Operatorでn回、1回失敗するごとにn * 1sずつ遅延リトライ

上で作成したmockのソースに retryWhen Operator を挟む

retryWhen でリトライ条件を指定することができるので、試行回数がretryCount以下だった場合は、

試行回数 * 遅延時間秒数待ってリトライするように条件指定をすることで実現できる。


const retryCount = 5;
const retryExecuteDuration = 1000;

// retryWhenを使用
source$()
.pipe(
retryWhen((errors) => {
return errors.pipe(
mergeMap((e, index) => {
if (index > retryCount) {
return throwError(e);
}

return timer((index + 1) * retryExecuteDuration);
}),
);
}),
)
.subscribe((value) => console.log(value), (e) => console.log(e));


試したソース

https://stackblitz.com/edit/rxjs-retry-vs-retrywhen?file=index.ts


参考