冪等タスク設計とは、あるタスクが一回実行されても複数回繰り返し実行されても、最終的に生み出される結果がまったく同じになるようにし、複数回実行されたことで余計な、あるはずのない結果が積み重ならないようにすることを指す。この概念は retry policy の安全性を直接支えている。再試行ポリシーは「もう一度実行すること」が安全な動作であると仮定しているが、この仮定はタスク自体が冪等である場合にのみ成り立つ。タスクが冪等でない場合(例えば「この注文に100円のボーナスを加える」のような、実行するたびに効果が積み重なる動作)、再試行は問題を修正しているのではなく、新しい問題を生み出している。ネットワークの問題でタスクが失敗し、成功していないと思って再試行したところ、実は一回目は既に書き込みに成功していて、二回分が積み重なりボーナスが二倍になってしまう。この種の誤りは元の失敗よりも発見しづらい。システムは完全に正常に見え、ただ数字が違っているだけだからだ。
この問題が必要とされるのは、retry policy と冪等設計が失敗処理の中で異なる二つの層を扱っており、前者さえあれば十分だと誤解されがちだからだ。再試行ポリシーは失敗後もう一度試すかどうかに答え、冪等設計はもう一度試すこと自体が安全かどうかに答える。冪等性を先に確認していなければ、再試行ポリシーの存在はむしろリスクを増幅させかねない。再試行の仕組みがなければ、失敗はせいぜいタスクが完了しなかっただけで済む。再試行の仕組みがあるのに冪等設計がなければ、失敗と再試行の組み合わせが、本来単純な失敗だったはずのものを、二重、三重にも副作用が積み重なった誤りへと変えてしまうことがある。だからこそ冪等設計は再試行ポリシーを設計する前に確認すべきものであり、両者を独立して扱うべきではない。順序を逆にすると、本来信頼性を高めるはずの仕組みが、逆に誤りを増幅させる仕組みになってしまう。
実務では核心となる確認方法が一つある。タスクの各段階について「この動作が二回実行されたら、一回実行した場合と結果が変わるか」を問うことだ。よくある非冪等な動作には、「加算」ロジックで数字を積み上げる(実行するたびにさらに加算される)、「新規追加」ロジックでデータを書き込む(繰り返し実行すると重複したレコードが生成される)、通知やメールを送信する(繰り返し実行すると受信者に二重に迷惑をかける)などがある。これらの動作を冪等にする一般的な方法は、「加算」を「ある絶対値に設定する」に変えること(「残高に100を加算する」ではなく、「残高を元の残高に100を加えた最終的な数値に設定し、この取引の一意な識別子を記録する」と書く)、「新規追加」を「同じ識別子のレコードが既に存在するか先に確認し、存在すればスキップ、存在しなければ追加する」に変えること、通知系の動作には明確な「送信済み」フラグを持たせ、再試行前にそのフラグを確認し、既に立っていれば送信しないことである。すべてに共通する核心的な考え方は、タスクを実行する前に「これは既に行われたことか」を確認させることであり、毎回まったく新しい動作として実行させないことだ。
あなたにとって、冪等タスク設計の本当の価値は、本来信頼性を高めるための仕組みである retry policy が、逆に誤りを増幅させる原因になるのではなく、本来果たすべき役割を実際に発揮できるようにする点にある。スケジュールタスクを設計する際に再試行ポリシーと組み合わせるつもりのあらゆる動作は、書き始める前に「この動作を二回実行したら、一回実行した場合と結果が変わるか」と問う価値がある。答えが「変わる」であれば、そのタスクは現時点では安全ではなく、冪等化の修正を先に行わなければ再試行ポリシーをそのまま適用してはならない。注意すべきは、冪等化の修正にはしばしば追加の状態記録(先に触れた一意な識別子、送信済みフラグなど)が必要になる点だ。この記録自体も正しく保存・照会されなければならず、記録の仕組み自体が信頼できなければ冪等設計は意味を失う。何かが既に行われたかを確認するその仕組み自体が、失敗しうる箇所であってはならない。
Stripe のような決済サービスの公式API文書では、決済リクエストを作成する際の推奨手法として冪等キー(idempotency key)が挙げられている。利用者は各リクエストに一意な識別子を添付でき、ネットワークの問題で同じリクエストが二重に送信された場合、サーバーはその識別子が既に処理済みであると認識し、二重に課金するのではなく元の結果をそのまま返す。この仕組みが存在する理由は、決済のような動作が一度重複して実行されると、その結果(二重課金)が単純なリクエスト失敗よりもはるかに対処しづらいからにほかならない。
メリットは再試行ポリシーを本当に安全に使えるようにする点で、タスクが何らかの理由で複数回実行されても、結果に余計な誤りが積み重ならない。デメリットは冪等化の修正に追加の設計コストがかかる点で、通常は一意な識別子や状態フラグといった仕組みを導入する必要があり、その仕組み自体も正しく実装・保存されなければならない。うまくできていなければ、冪等設計はシステムの複雑さを増やすだけで、本来の保護効果を実現できない。