堅牢なAPI操作のための冪等性の確保
James Reed
Infrastructure Engineer · Leapcell

はじめに
バックエンド開発の複雑な世界では、信頼性と回復力が最優先事項です。APIを設計・利用する際、ネットワークの不安定さや一時的なエラーから生じる一般的な課題があります。それは、リクエストをリトライする必要が生じた場合、どうなるかということです。読み取り専用操作(GET)の場合、リトライは一般的に安全です。しかし、POST(リソースの作成)やPATCH(リソースの更新)のようにデータを変更する操作では、単にリクエストをリトライするだけでは意図しない副作用につながる可能性があります。ユーザーが注文を送信し、ネットワークの不具合のためにリクエストがタイムアウトしたと想像してください。ユーザーまたはシステムが適切な保護なしに自動的に送信をリトライした場合、同じ注文が2つできてしまい、データの不整合や顧客の不満につながる可能性があります。そこで、冪等性という概念が重要になります。APIに冪等キーを実装することで、同じパラメータで操作を複数回呼び出すことが、1回呼び出すのと同じ効果を持つことを保証でき、システムをより堅牢でユーザーフレンドリーにすることができます。
冪等性とそのキーの理解
実装の詳細に入る前に、いくつかの主要な用語を明確にしましょう。
-
冪等性(Idempotency): APIの文脈では、操作は、それを複数回実行しても、1回実行した場合と同じ結果をもたらす場合、冪等であると言えます。これは、レスポンスが常に同一であることを意味するわけではありません(例:
POSTは、最初の成功した試行で201 Createdを返し、同じ冪等キーでの後続の試行では、リソースが既に存在するか操作が進行中であることを示す200 OKまたは202 Acceptedを返す可能性があります)。重要なのは、サーバー側の状態変更が同じであるということです。 -
冪等キー(Idempotency Key): これは、リクエストに付随する、クライアントが生成した一意の文字列です。サーバーはこのキーを使用して重複リクエストを検出します。サーバーが一定期間内に同じ冪等キーを持つ複数のリクエストを受信した場合、それらを元の操作のリトライとして識別し、再度処理することを避けることができます。
冪等キーの原則は比較的単純です。クライアントは特定操作の一意の識別子を生成し、それをリクエストと共に送信します。サーバーは、このキーを(しばしばリクエストの結果と共に)定義された期間保存します。後続のリクエストが同じキーで到着した場合、サーバーは以前に計算された結果を返すか、コアロジックを再実行せずに操作が既に正常に処理されたことを確認できます。
実装戦略
冪等キーの実装には、通常、次のステップが含まれます。
-
クライアント側のキー生成: クライアントは、実行しようとする各一意の操作に対して、Universally Unique Identifier(UUID)を生成する必要があります。このキーは、リクエストが送信される前に生成され、その特定操作のリトライのために再利用されるべきです。
-
サーバー側のキー保存とルックアップ:
- 前処理:
Idempotency-Keyヘッダー付きのリクエストを受信すると、サーバーはまず、このキーが以前に見られたかどうか、および関連付けられた操作が完了したかどうかを確認します。 - 実行(最初の試行): キーが新しい場合、サーバーはコアビジネスロジックの実行に進みます。正常な実行の前または後に、
Idempotency-Keyをリクエストパラメータ、レスポンス(ステータスコード、ボディ)、および有効期限タイムスタンプと共に保存します。 - 実行(後続のリトライ): キーが見つかった場合、サーバーは関連付けられた操作のステータスを確認します。正常に完了した場合、ビジネスロジックを再実行することなく、最初の成功した試行からの保存されたレスポンスを直ちに返します。元の操作がまだ進行中の場合、サーバーは(ポリシーに応じて)
409 Conflictまたは429 Too Many Requestsを返すか、完了を待つ可能性があります。
- 前処理:
-
冪等キーのためのデータストレージ: このデータは、永続的かつ効率的に保存される必要があります。一般的な選択肢は次のとおりです。
- Redis: 速度と有効期限(TTL)機能に優れており、有効期限付きのキーの保存に最適です。
- データベース(例:PostgreSQL、MySQL): 専用のテーブルにキー、リクエストパラメータ、レスポンスデータ、タイムスタンプを保存できます。これはより強い一貫性を提供しますが、大量のシナリオではRedisよりも遅くなる可能性があります。
コード例(概念 - Python FlaskとRedis)
Redisを冪等キーのストレージに使用する概念的なPython Flaskの例で説明します。
import uuid import json from flask import Flask, request, jsonify, make_response import redis import time app = Flask(__name__) # Redisに接続 redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) # 冪等キーのTTL(例:24時間) IDEMPOTENCY_KEY_TTL_SECONDS = 24 * 60 * 60 @app.route('/api/orders', methods=['POST']) def create_order(): idempotency_key = request.headers.get('Idempotency-Key') if not idempotency_key: return jsonify({

