SNS APIのページング処理は再試行を入れないと止まる

SNS APIのページング処理は再試行を入れないと止まる

フォロワー一覧のような大量データをSNS APIから取るとき、ページング処理は再試行を入れないと途中で止まります。実際に InvokeTimeoutError で処理が落ち、しかも例外の中身が空でログから原因を追えなかった話と、その対策2つをまとめます。

ページングは「1回でも失敗したら全部落ちる」構造になっている

フォロワー一覧のような大量データは、一度のリクエストでは取り切れないのでページングで取得します。ここが問題で、1,000人を超えると10回以上のリクエストになります。

10回以上のリクエストを直列でつなぐということは、途中で1回失敗しただけで処理全体が落ちるということです。9回成功していても、10回目でこけたらそこで終わりです。成功率が高いか低いかという話ではなく、回数が増えれば増えるほど「どこかで1回」が起きる構造になっている、という話です。

実測

1,000人超 → 10回以上のリクエスト
この10回のうち1回でも失敗すると処理全体が停止する

実際に InvokeTimeoutError で止まった

運用していて、実際に InvokeTimeoutError でページング処理が止まりました。ここまでは「まあ起きるよね」で済む話です。問題はこの先でした。

失敗したこと

ログに str(e) だけを記録していたため、例外の中身が空文字で出力されました。ログを見ても何が起きたのか分からず、原因にたどり着けませんでした。

エラーが起きたことは分かる。でも、なぜ起きたのかが分からない。ログとしてはほぼ無意味な状態です。例外を握ってログに出しているつもりでも、str(e) が空なら手がかりはゼロになります。

なお、なぜこの例外の str(e) が空だったのかについては、私の側では分かっていません。分かっていないので、ここは「そういうことが起きた」以上のことは書けません。

対策1:ページング呼び出しを再試行でくるむ

まず、ページングのAPI呼び出しそのものを再試行でくるみました。待ち時間は段階的に伸ばして、10秒・20秒・30秒と待って最大4回まで試す形です。

再試行の待ち時間(最大4回まで)
再試行の待ち時間(最大4回まで)
ポイント

再試行を入れる場所は「ページング全体」ではなく1回のページング呼び出しです。ここを守れば、10回以上のリクエストのうち1回こけても、そのページだけ取り直して続行できます。

対策2:ログに型名とトレースバックの最後の1行を入れる

もう一つは、ログの出し方を変えたことです。str(e) だけでは今回のように中身が空で完全に手がかりを失うので、

  • 例外の型名(今回でいえば InvokeTimeoutError)
  • トレースバックの最後の1行

この2つを必ず入れるようにしました。型名が残っていれば、少なくとも「タイムアウト系で落ちた」ところまでは即座に分かります。トレースバックの最後の1行があれば、どこで落ちたかも分かります。

注意

str(e) だけを記録する書き方は危険です。中身が空の例外に当たった瞬間、ログが「エラーが起きました」以上の情報を持たなくなります。動いているうちは気づけません。

やったこと・分かっていないこと

  • ページング呼び出しを再試行(10秒・20秒・30秒、最大4回)でくるんだ
  • ログに例外の型名とトレースバックの最後の1行を必ず出すようにした
  • str(e) が空文字だった理由は分かっていない
  • 4回の再試行で足りるかどうかも、断言できるだけの根拠は持っていない

再試行は「失敗しなくなる仕組み」ではなく、1回の失敗で全部を捨てないための仕組みです。そして再試行を入れてもいずれ限界回数まで失敗する日は来るので、そのときに原因が追えるログになっているかどうかが、結局いちばん効きます。

まとめ

  • フォロワー一覧のような大量データはページングで取得するが、1,000人超で10回以上のリクエストになり、1回の失敗で処理全体が落ちる
  • 実際に InvokeTimeoutError で停止し、str(e) が空文字だったためログから原因が分からなかった
  • 対策1:ページング呼び出しを再試行でくるむ(10秒・20秒・30秒、最大4回)
  • 対策2:ログには例外の型名とトレースバックの最後の1行を必ず入れる
  • str(e) だけの記録は、中身が空の例外に当たると手がかりを完全に失う
広告