自動再試行を503と504だけに限定した理由

生成AIのAPIが失敗したとき、何でも再試行すると無料枠を無駄に消費し、利用者の意図にも反します。再送する条件を2つの状態コードだけに絞った経緯を書きます。

APIの呼び出しが失敗したら再試行する、というのは定石です。 ただし何を失敗とみなすかを決めないと、定石がそのまま害になります。

このアプリでは、自動で再送するのを503と504のときだけに限定しました。 そこに至るまでの判断を書きます。

再試行してはいけないもの

429(枠の上限)

無料枠を使い切ったときに返る状態コードです。 ここで再送すると、上限に達しているのにさらに送ることになります。

枠が回復するのは翌日なので、数秒待って送り直しても意味がありません。 それどころか、再送も送信回数として数えられるため、 翌日の枠を先食いする可能性すらあります。

正しい対処は再送ではなく、 「上限に達したこと」と「いつ回復するか」を利用者に伝えることです。

通信の切断

電波が届かない場所に移動した、機内モードにした、といった状況です。 ここで自動再送すると、利用者が意図していない送信が起きます。

通信が切れているときに何度も送ろうとして、 復帰した瞬間にまとめて送信される、という挙動は避けたいものです。 再接続したことを画面に出して、送るかどうかは利用者に決めてもらう形にしました。

利用者による中止

「中止」ボタンを押したあとに再送するのは、明確に意図に反します。

自明に見えますが、実装上は注意が要ります。 再試行の待機中に中止されるケースがあるためです。 600ミリ秒待ってから送り直す処理の途中で中止が押された場合、 待機を打ち切って、2回目の送信を行わずに終える必要があります。

待機処理に中止の信号を渡し、 待っている間に中止されたら即座に抜けるようにしました。

再試行してよいもの

残ったのが503と504です。

どちらもサービス側が一時的に応答できない状態を示します。 利用者の操作にも、枠の状況にも問題がなく、 少し待てば成功する見込みがあるのはこの2つだけでした。

再送は1回だけにしています。 2回、3回と重ねると、失敗し続けたときに待ち時間が伸び、 その間ずっと利用者が画面の前で待つことになります。 1回試して駄目なら、原因を伝えて撮り直しや再実行を促すほうが早く解決します。

Retry-After をそのまま信じない

サービス側が「何秒後に再試行してください」と返してくることがあります。 これに素直に従うと、指定が長かったときに画面が固まったように見えます

数分待てと言われて本当に数分待つのは、 バックグラウンドで動く処理なら正しくても、 利用者が画面の前で結果を待っているアプリでは正しくありません。

そこで、指定された待ち時間には上限を設けました。 上限を超える指定が来た場合は、待たずに失敗として扱い、状況を伝えます。

Retry-After は秒数で来る場合と日時で来る場合があります。 どちらの形式も解釈したうえで、上限に丸めています。 値が無い場合の既定値も決めてあります。

再送も使用回数に数える

内部で自動的に再送した分も、使用回数として記録しています。

ここを記録から漏らすと、画面の表示と実際の消費がずれます。 利用者から見て「10回しか使っていないのに枠が足りない」という状態になり、 表示そのものが信用されなくなります。

自動でやったことほど、記録に残す必要があります。 利用者が見ていないところで起きるからです。 使用回数の数え方については無料枠の使用回数を数える設計に書きました。

判断の整理

状況自動で再送するか理由
503・504する(1回だけ)一時的な障害。待てば成功する見込みがある
429(枠の上限)しない送っても失敗し、枠をさらに消費する
通信の切断しない利用者が意図していない送信になる
利用者による中止しない明確に意図に反する
4xx(リクエストの誤り)しない同じ内容を送っても結果は変わらない

再試行の設計で決めるべきは「何回試すか」ではなく「何を試すか」でした。 回数の調整に意味があるのは、対象を正しく絞ったあとの話です。

関連する記事

最終更新日: