同じ通知が2回届くことがあります。送る側が返事を受け取れず、念のためもう一度送るからです。
1000件のうち65件が二重に届く状況を走らせました。何も対処しないと金額の合計が84,200円ずれます。IDを覚えるだけで0件になります。
同じ通知が二重に届く状況を走らせました。何も対処しないと、そのぶんが合計に乗ります。
冪等性が要る場面を確かめるため、実際に走らせて測りました。通知1000件のうち、5%の割合で同じものが2回届く状況です。
受け方を3通り用意しました。何もしない場合、処理したIDをすべて覚える場合、直近100件だけ覚える場合です。
['処理したIDを覚える', () => {
const seen = new Set();
return (e) => (seen.has(e.id) ? false : (seen.add(e.id), true));
}]
通知 1000件のうち 65件が二重に届いた(合計 1065件) やり方 処理した件数 二重に処理した件数 金額の合計 何もしない 1065件 65件 1,383,900円 処理したIDを覚える 1000件 0件 1,299,700円 直近100件だけ覚える 1000件 0件 1,299,700円 本来の金額の合計: 1,299,700円
上の表を見てください。何もしないと1,383,900円になりました。本来は1,299,700円です。
差額は84,200円です。65件ぶんの処理が余計に走った結果です。
金額なら気づけるかもしれません。ところがこれが通知メールなら二重送信、在庫なら二重引き当てになります。気づきにくい形で表に出ます。
2行目と3行目はどちらも二重処理が0件です。処理したIDを覚えて、2回目を弾いただけです。
Stripeも、この仕組みを同じ操作を誤って二度行うことなく、安全に要求をやり直すためのものだとしています。送り直しを安全にするための仕掛けです。
対処しないぶんが、そのまま合計に乗る。
for safely retrying requests without accidentally performing the same operation twice原文Stripe API リファレンス「Idempotent requests」 この内容の有効期限2027-02-18
同じ処理を何度受けても、結果が1回受けたときと変わらない性質です。送り直しを安全にするための前提になります。
冪等性とは、同じ処理を何度受けても結果が変わらない性質のことです。
送る側は、返事が返ってこなかったとき処理されたのかどうか分かりません。処理された可能性もあれば、届かなかった可能性もあります。
このとき安全側に倒すと送り直しになります。受ける側が二重を弾ける前提があって初めて、送り直しが安全になります。
見分けるには一意の鍵が要ります。Stripeの説明では、呼び出す側が鍵を作り、その鍵で同じ要求の送り直しをサーバが見分ける形です。
鍵の作り方は任せられていますが、推測されない十分な長さの文字列が推奨されています。メールアドレスのような個人を特定できるものは避けるよう案内されています。
1番目がいちばん確実です。処理の記録と鍵の記録がずれないためです。速さを取るなら2番目ですが、ずれる場面を考えておく必要があります。
二重に届く割合は種を固定した乱数で決めています。実際の割合は、送る側の作りと回線の状態で変わります。ここで見せているのは、対処しなかったぶんがそのまま結果に乗るという関係です。送り直しの側の設計はリトライ設計の記事で扱っています。
A client generates an idempotency key, which is a unique key that the server uses to recognize subsequent retries of the same request.原文Stripe API リファレンス「Idempotent requests」 この内容の有効期限2027-02-18
鍵を永久に覚えることはできません。どれだけ覚えるかを決めることが、そのまま設計になります。
冪等性を保つには処理済みの鍵を覚えます。ところが覚える量は増え続けます。どこかで捨てる必要があります。
前の節の3行目は直近100件だけ覚える方式でした。この計測では二重処理は0件です。
0件になったのは、二重の到着がすぐ後ろに続いたからです。100件離れて届いた場合は弾けません。
Stripeも、元の鍵が消されたあとに同じ鍵が再利用された場合は、新しい要求を生成するとしています。期間を過ぎた再送は新規として扱われます。
Stripeの案内では、24時間以上経った鍵はシステムから自動で消してよいとされています。1つの目安になります。
決め方は送る側が最大どれだけ遅れて送り直すかです。相手の送り直しが最大1時間なら、1時間より長く覚えれば足ります。
4番目が抜けがちです。二重処理は例外を出さずに通ります。前の節でも、何もしない場合に失敗した処理は1件もありませんでした。
覚える期間が、相手の送り直しより短いと穴が開く。
We generate a new request if a key is reused after the original is pruned.原文Stripe API リファレンス「Idempotent requests」 この内容の有効期限2027-02-18
同じ課題を持つ会社にとって、動いている設定は「作る時間」を買えるということです。ServiceDockは自作のワークフローやテンプレートを出品できるマーケットプレイスです。手数料や出品の流れは出品者向けページにまとまっています。
出品の仕組みを見る