日本語

開発者ツール · UUID ジェネレーター

冪等キー: クライアント生成の UUID を使用して再試行を安全にする

· なぜそれが重要なのか

uuid 暗号化 ブラウザ API

クライアントが同じ冪等キーを 2 回送信し、サーバーがキャッシュされた応答を返すことを示すシーケンス図
オリジナル ToolAcre ベクトル イラスト

支払いリクエストがタイムアウトになると、リクエストが完了したかどうかがわかりません。冪等キーを使用すると安全に再試行できます。CSPRNG によって生成された UUID が自然キーです。この投稿では、パターンを端から端まで説明します。

顧客に 2 回請求された可能性があるタイムアウト - 障害モードの冪等性キーは修正するために存在します

支払いリクエスト中のタイムアウトは、クライアントとシステムに真の不確実性をもたらします。 HTTP クライアントは応答の待機をあきらめましたが、接続が閉じられるかタイムアウトになる前に、支払いサーバーがトランザクションを処理した可能性があります。同じリクエストを再試行すると、顧客に 2 回請求される可能性があります。再試行しないと、支払いは完了しません。支払いシステムは不幸な中間点に陥っています。顧客のお金がなくなっているかもしれない、明日届くかもしれない、処理キューに詰まっているかもしれない、またはアカウントからまったく出ていないかもしれないのです。この曖昧さは金融システムにとって容認できません。

冪等キーの仕組み — サーバーは最初の応答をキーに保存し、それを繰り返し再生します。

冪等キーは、再試行を安全かつ決定的に行うことで、この問題をエレガントに解決します。クライアントは、支払い、送金、請求などのインテントごとに一意のキーを生成し、それをすべてのリクエストに含めます。サーバーは支払いを処理し、そのキーの下に応答をキャッシュし、キーと結果の両方を保存します。保持期間内に同じキーが再度到着した場合、サーバーは支払いを再度処理せずに、キャッシュされた応答を再生します。まったく同じキーが何度送信されても​​、常に同じ結果が得られることがわかっているため、顧客は自信を持って再試行できます。このパターンにより曖昧さが排除され、再試行ロジックが安全になります。

最初の試行の前に生成 — リクエストが終了する前にキーが存在し、再試行時にそのまま再利用される必要がある理由

このパターンは最新の HTTP 仕様よりも古いものですが、広範な経済的損失や二重請求による顧客からの苦情を受けて、支払いで有名になりました。すべての支払い API と多くの Web サービス API が冪等性キーをサポートするようになりました。 CSPRNG によって生成された UUID は、推測不可能で、クライアント間での調整がなくても一意であり、サーバー側の割り当てや中央の権限を必要としないため、キーとして自然に選択されます。クライアントは最初の試行の前にそれを生成し、再試行のたびにそのまま再利用し、毎回同じ応答を受け取ります。キー生成を調整するためにサーバー側の状態は必要ありません。

カウンタやペイロード ハッシュではなく、ランダムな UUID を使用する理由 — 調整のない一意性と、インテント間での偶発的な再利用がない

再試行時にキーを生成すると冪等性を確保するには遅すぎるため、キーはリクエストがクライアントから送信される前に存在する必要があります。最初のリクエストが成功して顧客に請求された場合、再試行時に新しいキーを生成すると問題が隠蔽され、再度請求されます。クライアントは、最初の試行の前にキーをコミットし、それをメモリまたは永続ストレージに保存し、タイムアウトまたは再試行が必要になった場合に同じキーを再利用する必要があります。手動 API テストの場合、ToolAcre ジェネレーターは、curl または REST クライアントに貼り付け、コピーして複数のリクエスト間で再利用して冪等性の動作をテストできるキーを生成します。

スコープと有効期間 - 操作ごと、アカウントごとのキー、およびサーバーがキーを記憶する期間

なぜ冪等キーにハッシュやシーケンシャルカウンターではなく UUID を使うのでしょうか?リクエスト ペイロードのハッシュは直感的に見えます。同一のペイロードは同一のハッシュを取得し、したがって同一のキーを取得します。しかし、ハッシュはこのユースケースでは弱いです。異なる量、異なる受信者、または異なるパラメータを持つ 2 つのほぼ同一のリクエストは完全に異なるハッシュを生成し、その結果、別々の料金が発生するためです。これは正しいですが、必要なすべての保護を提供するわけではありません。シーケンシャル カウンタには調整と分散状態が必要です。2 つのクライアントの両方がインフラストラクチャ上でカウンタ ベースのキーを生成すると、それらのカウンタが衝突する可能性があります。 UUID は中央権威を必要とせず、推測不可能であり、常にインターネット全体で偶然に衝突する可能性は極めて低いです。

動作した例 - 同じキーを使用した再試行シーケンス。毎回、クライアントが送信する内容とサーバーが返す内容を示します。

サーバー側の実装では、応答をキーの下に保存し、繰り返しの際にキャッシュされた応答を返します。複雑なのは、キーを記憶する保持期間、記憶するキーの数、同じキーによる 2 つの同時リクエストによる支払いの 2 回処理を防ぐロック方法、キーを忘れたときのクリーンアップなど、実際的な運用上の質問を決定する際にあります。これらは、UUID ジェネレーターの範囲外のストレージと信頼性に関する問題です。クライアントの仕事は、適切なキーを生成し、再試行時にそれを再利用することです。サーバーの仕事は、キャッシュを正しく、永続的に実装することです。

これでカバーされないもの — パターンの実装に必要なサーバー側のストレージとロック (別の設計)

動作例は、実際の典型的なシーケンスを示しています。モバイル アプリは、冪等性をサポートする API を使用して友人に送金する必要があります。リクエストを送信する前に、アプリはローカル暗号化ライブラリを使用して UUID を生成するか、テスト目的で ToolAcre ジェネレーターから UUID を取得します: 3fa85f64-5717-4562-b3fc-2c963f66afa6。アプリは、JSON 本文と HTTP ヘッダー Idempotency-Key: 3fa85f64-5717-4562-b3fc-2c963f66afa6 を含む POST リクエストを /transfers に送信します。サーバーは転送を処理し、 3fa85f64-5717-4562-b3fc-2c963f66afa6 → {status: "success", transferId: "xfer-12345"} をキャッシュに保存し、結果とともに 200 応答を返します。

要点: 1 つのインテント、1 つのキー — ToolAcre ジェネレーターは、手動で統合をテストするときにキーとして使用できる CSPRNG ベースの UUID を提供します。

ネットワークがタイムアウトし、クライアントは最初の試行からの応答を確認できません。アプリは、新しい UUID を生成せずに、同じ冪等キーを使用して同じリクエストを再試行します。サーバーはキャッシュ内のキーを認識し、キャッシュされた応答を見つけて、新しい転送を処理したり、顧客に再度請求したりすることなく、すぐに {status: "success", transferId: "xfer-12345"} を返します。この操作は冪等であり、再試行すると毎回同じ観察可能な結果が生成されます。機能するテストの場合、ToolAcre ジェネレーターがキーを提供できます。 UUID を生成し、それをヘッダーに含め、応答を観察し、同じキーで再送信して、サーバーがキャッシュを正しく実装していることを確認します。