冪等性エンジン:トランザクションの整合性を保証する

SchemaBridge Team · 2025-12-22 · Idempotency, Consistency, Transactions

断片化した世界においてトランザクションの整合性を確保する。分散システムの安全性を支える数学的パターン。

「二重課金」の悪夢:分散システムがリトライを嫌う理由

不安定なネットワークと短命なクラウドリソースが支配するこの世界では、障害は稀な出来事ではなく、常態です。あらゆるシニアエンジニアが「孤児化したアクション」の悪夢を経験しています。それはささいなネットワークの不調として始まり、致命的なデータ破損や金銭的損失として終わるシナリオです。これはCTOたちの眠りを妨げる、分散システムにおける典型的な障害シナリオです。

1. リクエスト: あなたのオーケストレーターがStripeへ「課金」リクエストを送るか、FedExへ「発送」リクエストを送ります。

2. 成功: サードパーティのAPIがリクエストの処理に成功し、顧客のカードに課金するか、配送ラベルを印刷します。

3. パーティション: 復路でわずかなネットワークの不調が発生します。APIからのレスポンス――重要な確認応答――がワーカーマシンに届きません。

4. リトライ: タイムアウトを検知したワーカーは、標準的な運用に従って処理が失敗したと正しく仮定します。リトライポリシーに従い、再度リクエストを送信します。

5. 重複: このAPIにはこの特定の意図に対する一意なIDが与えられていないため、リクエストを再び処理してしまいます。APIはこれを新しいトランザクションとして認識します。顧客は二重に課金され、あるいは同じ注文に対して2枚の配送ラベルが発行されてしまいます。

これはコードロジックの不具合ではありません。ID(Identity)の不備なのです。時間と空間を超えて特定の意図を一意に識別する手段がなければ、あなたのシステムは接続をリトライするたびに、顧客のデータとお金を賭けの対象にしているようなものです。

分散トランザクションは終わった:冪等性こそが生き残る

モノリシックなアーキテクチャでは、整合性を保証するために2フェーズコミット(2PC)やグローバルな分散ロックに頼ります。これらのツールにより、複数の操作を単一の真実の単位として扱うことができます。しかし、SaaS API、サーバーレスワーカー、多言語混在のマイクロサービスが乱立する断片化した世界では、グローバルトランザクションは幻想にすぎません。それらはスケールせず、莫大なレイテンシをもたらし、大半のサードパーティプロバイダーはそれをサポートしていません(そして今後もサポートすることはないでしょう)。それらは組織の境界を越えて実現することが物理的に不可能な「ロック」を要求します。

分散環境における整合性を実現する唯一の現実的な道は冪等性(Idempotency)です。数学的に言えば、ある操作は、複数回適用しても最初の適用を超えて結果が変わらない場合に冪等であると言えます。代数的に表すと f(x) = f(f(x)) です。エンジニアリングの言葉で言えば、システムが何度失敗し、何度リトライしても、最終結果は常に正しいものになる、ということです。

SchemaBridgeのアプローチ:プラガブルな冪等性戦略

多くのチームは、UUIDを手動で生成しデータベースに保存することで冪等性の問題を解決しようとします。これは「キー管理の罠」です。冪等性キーを管理するため(生成し、保存し、確認し、最終的にパージするため)に、実際のビジネスロジックと同じくらいの量のコードを書くことになります。これはPart 1で論じた「グルーコードの危機」の別の形にすぎません。

SchemaBridgeでは、IDの負担をインフラストラクチャ層に移します。手動での管理を開発者のタスクリストから取り除くため、プラガブルな冪等性戦略を用いてIDを自動的に管理します。

戦略の解剖:柔軟なID

SchemaBridgeの冪等性戦略では、IDをどのように導出するかを定義できます。ランダムなUUIDに頼るシステムもありますが、私たちのデフォルト戦略では以下が可能です。

1. ワークフローインスタンスID: 特定のジャーニーに紐づく、一意で永続的なID。これにより、キーが特定のユーザーやアクションに属することが保証されます。

2. 頂点ID(Vertex Identity): グラフ内の特定のステップ(例:「ChargeCustomer」)。

3. 設定可能なロジック: IdempotencyStrategy インターフェースを通じて、厳密に決定論的なハッシュ化が必要な場合には、ペイロードの内容からキーを導出するカスタムロジックを注入できます。

この戦略はエンジンによって処理されるため、ネットワークタイムアウト、マシンのクラッシュ、あるいは手動での再起動のいずれが原因であっても、あるステップがリトライされた際に、結果として得られるキーは安定したままになります。

ハッシュ衝突理論:10億件のトランザクションでも安全か?

セキュリティを重視するアーキテクトからよく寄せられる質問は、「もし2つの異なるトランザクションが同じハッシュを生成したらどうするのか?」というものです。これはハッシュ衝突(Hash Collision)として知られ、数十億件のイベントを処理する大規模システムにおいては軽視できない懸念事項です。

安全性の数学

SchemaBridgeはワークフローIDと頂点IDを組み合わせた一意性に依拠しています。ワークフローIDはグローバルに一意(UUIDv4)であり、頂点IDはワークフロー定義内で一意であるため、そのペアは特定の実行インスタンスにおいて一意であることが保証されます。

レガシーAPIへの対応:「読み取り-検証-書き込み」パターン

残念ながら、多くのレガシーシステムやニッチなSaaSプロバイダーはネイティブに冪等性キーをサポートしていません。Idempotency-Key ヘッダーが存在しないのです。こうした「非冪等」なエンドポイントに対して、SchemaBridgeは特殊な耐久性パターン、読み取り-検証-書き込み(Read-Verify-Write)をサポートしています。

単一の「アクション」頂点の代わりに、エンジンによってオーケストレーションされる3ステップのシーケンスを使用します。

1. 検証頂点(読み取り): エンジンはまず下流システムに対して、そのレコードがすでに存在するか、そのアクションがすでに実行済みかを問い合わせます(例:GET /orders?external_id=123)。この呼び出しはエンジンの決定論的なIDによって駆動されます。

2. 条件分岐: JSONata(Part 2参照)を使って、エンジンがレスポンスを確認します。注文がすでに存在すれば「スキップ」状態へ遷移し、存在しなければ処理を続行します。

3. アクション頂点(書き込み): 検証頂点が否定的な結果を返した場合にのみ、エンジンは実際の書き込み操作(POST /orders)へと進みます。

なぜこれが耐久性を持つのか

このシーケンス自体が耐久性のあるワークフローでラップされているため、エンジンは「チェック」と「アクト」の間の遷移が確実に処理されることを保証します。チェックとアクトの間でシステムがクラッシュした場合、エンジンは状態を復元し、処理を進める前に再検証を行うよう設定できるため、競合状態のウィンドウをほぼゼロにまで最小化できます。

「キー管理」の罠:自作の冪等性がスケールで破綻する理由

多くのエンジニアリングチームは、メインのデータベースに「冪等性テーブル」を構築しようとします。これは最終的に速度と信頼性を殺す、3つの重大な問題を生み出します。

1. 書き込みのボトルネック: あらゆるAPI呼び出しが、トークンを記録するためのデータベース書き込みを必要とするようになります。高負荷時には、冪等性テーブルが競合の主要な発生源となります。単一のリトライを安全にするためだけに、アプリケーション全体を遅くする行レベルロックが発生します。

2. クリーンアップの複雑性:ゴミの問題: 冪等性キーは永遠に必要なわけではありません。古いキーを削除するためのバックグラウンドプロセスやTTL(生存期間)が必要です。削除が積極的すぎると、遅延した、リトライされたタスクで二重課金のリスクが生じます。遅すぎると、データベースはクラッシュするまで肥大化し続けます。このバランスを管理することは重大な運用負担です。

3. 分散状態の不整合: データベースへの書き込みは成功したがAPI呼び出しが失敗した場合はどうなるでしょうか? あるいは、API呼び出しの後、データベースが「完了」と更新される前にワーカーがクラッシュした場合はどうでしょうか? 結果として、手動での介入を必要とする分散状態の不整合が生じてしまいます。

SchemaBridgeは、実行エンジンと緊密に統合された内部の最適化されたキーストアを用いることで、これらの問題を排除します。キーはワークフローのアトミックな状態コミットの一部として永続化され、ワークフローが自然に終端状態に達すると自動的に管理・破棄されます。これは「IDのためのガベージコレクション」です。

クライアントサイド vs. サーバーサイドのトークン生成

トークンはどこで生成されるべきでしょうか?

トークンを意図の発生源(Source of Intent)(ワークフロー)で生成することで、データが中間のゲートウェイやプロキシを何回経由しようとも、エンドツーエンドの整合性を確保できます。

比較表:整合性モデル

| 特徴 | データベース制約 | 自作の冪等性テーブル | SchemaBridgeエンジン |

| :--- | :--- | :--- | :--- |

| 適用範囲 | 内部DBのみ | 自社サービスのみ | あらゆるサードパーティSaaS API |

| 永続性 | 永続的 | 手動TTL | ライフサイクル対応 |

| オーバーヘッド | 高い(ロック) | 高い(二次的なIO) | 低い(アトミックな状態コミット) |

| 可視性 | 不透明(DBログ) | 乏しい(独自ログ) | 可視(トレース可能なグラフ) |

| 信頼性 | 高い | 低い(バグが発生しやすい) | 高い(インフラレベル) |

エキスパートのヒント:冪等性チェックリスト

1. タイムスタンプは絶対に使わない: キーはデータに基づくべきであり、時刻に基づくべきではありません。

2. キーのスコープを分ける: 「Shipping」用のキーが、入力が同じであっても「Billing」用のキーと衝突しないようにします。

3. 409コンフリクトを処理する: APIが409(コンフリクト)を返した場合、入力が一致していればそれを成功として扱うのが理想的です。

4. 耐久性のある履歴を使う: トランザクションが完全に終端に達し、監査済みであると確信できるまでキーを破棄しない。

5. トークン生成を自動化する: 開発者が冪等性キーを追加することを覚えておかなければならないなら、いずれ忘れます。それをエンジンに任せましょう。

まとめ:IDこそが真実の背骨である

分散システムでは、ネットワークを信頼することはできず、時計を信頼することもできず、レスポンスを信頼することもできません。本当に信頼できるものはただ一つ、ID(Identity)です。

冪等性エンジンは、SchemaBridgeの「耐久性のある真実」という約束の基盤です。これらのキーの生成と管理を自動化することで、手動での記録管理のオーバーヘッドなしに複雑で信頼性の高いトランザクションを構築できるようになります。私たちは「二重課金の悪夢」を、アーキテクチャによって解決済みの問題へと変えます。

Part 5では「マージ」頂点を取り上げ、競合状態を発生させずに並列ブランチ間で状態を同期する方法を見ていきます。「ロングテール」問題と、100万件の並列イベントを単一の一貫した状態へと調整する方法を探ります。

詳しく見る