PaPoo
cover

iOSの再試行が“二重実行”を生むときに考えたこと

読んでまず思ったのは、これは「失敗した通信をもう一回やればいい」という単純な話では全然ない、ということだった。iPhone側ではただタイムアウトしたように見えても、サーバー側ではもう処理が走っているかもしれない。しかも agent や tool を呼ぶ話になると、その一回の重複がそのまま課金、在庫確保、メッセージ送信の二重実行につながる。ここを雑に扱うと、あとで帳尻を合わせるのがかなり苦しいはずだ。

特に腑に落ちたのは、「exactly once」をあれこれの基盤に期待しすぎない、という姿勢だった。Kafka には Kafka の中での保証がある。でも iPhone、HTTP、agent、MCP、DB、外部APIまでまたぐと、その保証は途中で切れる。記事が言っている effectively-once という考え方は、きれいではないけれど現実的だと思う。要するに、何かが再試行されても同じ operationId で拾い直せるようにしておく。全部を一発で正確にするのではなく、重複しても最後は1つに収束するように設計する。

この手の話で地味に重要なのに、軽く見落とされがちなのが「operationId をいつ作るか」だと感じた。ネットワーク送信の直前では遅い。ローカルで“やる”と決めた時点で durable intent として残しておかないと、再送時に同じ意図だと証明できないからだ。LangGraph の checkpoint や MCP Tasks の durable handle も、結局は「再開できる」ための仕組みであって、「二重に実行しない」こと自体は別問題なんだな、と改めて思った。

逆に、App Attest まで idempotency token の代わりにしてはいけない、という指摘はかなり大事だと思う。App Attest は正規の端末から来たリクエストかを見るためのもので、ビジネス上の一意性を表すものではない。ここを混ぜると、セキュリティのための材料と、重複排除のための材料がごちゃっとしてしまう。似た役割に見えるけれど、目的が違う。記事を読んでいて、その切り分けが一番きれいに整理されていたように思う。


参考: Preventing Duplicate Agent Execution on iOS

同じ著者の記事