t0mmy

2020年12月30日に参加

学習履歴詳細

API デザインパターン 26章 リクエストの重複実行回避 読了

やったこと

  • API デザインパターン 26章 リクエストの重複実行回避 読了

学んだこと

ポイント

  • どのようなときに、リクエストの重複実行回避が必要になるか
  • どうやって重複回避を実現するか
  • リクエスト識別子は自動生成するべきか、ユーザーに設定させるべきか
  • キャッシュは最新化するべきか

    学び

  • リクエストの重複実行回避機能は、冪等性を持たず、重複させたくない処理に対して必要となる

  • 以下二つをAPI側でキャッシュすることで、リクエストの重複を検証できる

    • リクエスト識別子
    • リクエストボディのフィンガープリント
  • リクエスト識別子は、ユーザーに設定させるべき

    • 利用者が、常に再試行を意図しているとは限らず、意図を知る方法がない
    • 「重複実行を回避したい」という利用者の明確な意図を知るためにも、識別子は利用者の手で設定してもらう
  • キャッシュは最新化するべきではない

    • キャッシュ最新化により、再試行処理をリクエストしたクライアントにとって、意図しない結果が生成される可能性がある
    • これは再試行処理の目的から逸脱している ## メモ

ネットワーク経由のAPI リクエストは、失敗する可能性を常にはらんでいる。
場合によっては、APIリクエスト失敗を再試行処理が必要になる。
冪等性を持つリクエストなら問題ないが、冪等性のないリクエストの場合、処理を重複させない、安全な再試行方法が必要となる。
Web API で APIの実行に失敗した時に、安全にリクエストを再試行する仕組みを見ていく。

対象とする問題

ネットワーク経由のAPI リクエストは、失敗する可能性を常にはらんでいる。
本章では、APIレスポンスが正常に届かない状況を取り扱う。

レスポンスが届かなければ、基本的に再試行することになる。
冪等性を持つリクエストは再試行すればよい。
冪等性を持たないリクエストを何も考えずに再試行すると、リソースが重複する恐れがある。
リソースの重複を回避するためには、重複リクエストを防ぐ仕組みが必要となる。

概要

重複リクエストを避けるため、一度だけ実行されることを保証したいリクエストに、一意な識別子(以降、リクエスト識別子と呼称する)を付与する方法を考える。
識別子を比較することで、リクエストが既に処理されたか検証する。

重複を発見した場合、エラーを返すのではなく、正常に終了した時のレスポンスを返す。

実装

リクエスト識別子

リクエスト識別子は、クライアント側のリクエストプロパティに含める。
そのため、クライアント側でリクエスト識別子を決定する。

任意の識別子を許してしまうと、あまりよくない識別子を使用される恐れがあるため、API側である程度誘導すると良い。

  • フォーマット指定や、ランダムな値の入力を推奨(または強制)する

API サーバー側は、無効なリクエスト識別子が入力されたリクエストに対して、 400 エラーを返す。

導出した識別子は使わない

導出した識別子とは、リクエストから算出するような識別子のこと。
クライアントに識別子を設定させず、処理の裏側でリクエストから識別子を算出し、設定する方法。
つまり、デフォルトで重複実行を回避する戦略。

しかし、利用者の意図を確実に知る方法はないため、デフォルトでは重複実行を回避するべきではない。
「重複実行を回避したい」という利用者の明確な意図を知るためにも、識別子は利用者の手で設定してもらう。

レスポンスをキャッシュする

API側は、以下をキャッシュする。

  • 「処理に成功して生成したレスポンス
  • 上記レスポンスに対応するリクエストのリクエスト識別子

新規リクエストが届いたら、届いたリクエストのリクエスト識別子がキャッシュされていないかチェックする。
キャッシュされていれば、対応するレスポンスを返却する。
キャッシュされていなければ、処理を行い、結果をキャッシュした後にレスポンスを返す。

キャッシュの容量は有限のため、一定期間で削除することになる。
基本的に、失敗したらすぐに再試行されると考えるべき。
最初は5分程度に設定し、利用者の行動パターンから、キャッシュ期間を調整すると良い。

一貫性

キャッシュの導入は、最新ではないデータを扱う可能性が生まれることを意味する。
キャッシュを最新化して処理を行うと、再試行を試みたクライアントの意図しないレスポンスが生成される可能性がある。
リクエストの再試行という目的を考えると、一貫性を犠牲にしてでも、キャッシュを最新化しない方が良い。

リクエスト識別子の衝突

クライアント側でリクエスト識別子を指定してもらう以上、リクエスト識別子の衝突は十分発生し得る。

この問題は、リクエストボディのフィンガープリントを比較することで解決できる。

  1. レスポンスキャッシュ時、リクエストボディのハッシュ値をフィンガープリントとして一緒にキャッシュする
  2. APIは、再試行リクエストを受け取ると、以下を比較する
    • リクエスト識別子
    • リクエストボディのフィンガープリント
  3. 両方一致している場合、同一のリクエストとみなし、キャッシュを返却する
  4. 片方でも一致しない場合、 409 エラーを返す

トレードオフ

重複実行回避機能の提供と、リクエストの重複実行回避ロジック実装によるAPIの複雑さがトレードオフ。

重複実行回避機能は、常に必要というわけではない。
必要に応じて追加するのでも十分。


扱うこと

  • リクエスト識別子を使用して重複したリクエストの実行を防ぐ方法
  • リクエスト識別子が衝突しないようにし、結果を混乱させないようにする方法
  • リクエストとレスポンスの一貫性とのキャッシュとのバランス

まとめ

  • リクエストはいつでも失敗する可能性がある
    • 従って、APIから、確認されたレスポンスが送られてこない限り、リクエストが処理されたかどうかを確認する方法はない
  • 「リクエスト識別子」
    • APIを使うクライアントによって生成される
    • API リクエストの識別子であり、リクエストの重複実行を識別できる
  • リクエスト識別子と、そのリクエストのレスポンスをキャッシュすることで、重複リクエストを排除する
    • キャッシュは永続保管することはなく、有効期限や無効化パラメーターを用いてキャッシュを制御する
    • 有効期限と無効化パラメーターを慎重に決める必要がある
  • リクエスト識別子と一緒にリクエストの内容のフィンガープリントもチェックする必要がある
    • リクエスト識別子の衝突による異常な動作を避けるため
WebAPI

2023年12月10日(日)

1.0時間