HN 日本語サマリー

← 一覧へ戻る
プログラミング

Postgresトランザクションは分散システムのスーパーパワーである

Postgres transactions are a distributed systems superpower (dbos.dev)

192 pointsby KraftyOne83 コメント

要約

この記事では、ワークフローの状態とアプリケーションデータを同じPostgresデータベース内に共置することの利点を解説しています。これにより、単一のデータベーストランザクション内で両方を更新できるようになり、部分的な障害を防ぎ、冪等性や原子性といった分散システムにおける複雑な問題を簡素化できます。

全文翻訳

数週間前、私たちは「単にPostgresを使え」と耐久性のあるワークフローについて書きました。その投稿は多くの議論を呼びましたが、誤解も生みました。私たちは、ワークフローエンジンがPostgresに状態を保存することを意味しただけではありません。ワークフローシステムは、アプリケーションと同じPostgresデータベース内に存在でき、しばしばそうすべきだと私たちは考えています。一見すると、これは良い考えではないように思えます。それらの懸念は分離されるべきではないでしょうか?ワークフローの状態は一つのデータベースに、アプリケーションデータは別のデータベースに置かれるべきではないでしょうか?そうではないかもしれません。分散システムにおいて、共置はスーパーパワーです。ワークフローメタデータとアプリケーションデータが同じPostgresデータベース内に存在する場合、それらは同じデータベーストランザクションで更新できます。これは部分的な障害を不可能にし、すべてのエッジケースを正しく処理するワークフローを構築することをはるかに容易にします。この記事では、それがなぜ可能であるか、そしてトランザクションがいかに冪等性や原子性のような難しい問題を簡素化できるかを説明します。トランザクションステップによる冪等性分散システムにおける基本的な課題の一つは、特にデータベース状態を変更する操作において、冪等性です。耐久性のあるワークフローは、各ステップが完了した結果をチェックポイントとして保存することで耐障害性を実現します。ワークフローが中断された場合、最初からではなく、最後にチェックポイントされたステップから再開します。しかし、ワークフローはステップを完了した後、チェックポイントを記録する前に中断される可能性があります。復旧時、そのステップが既に実行された記録がないため、再度実行してしまいます。その結果、耐久性のあるワークフローだけでは冪等性の問題は解決されません。ワークフローエンジンは通常、重複した副作用なしに安全に再試行できるように、ステップが冪等であることを要求します。例えば、銀行口座に(お金を)加算するステップを考えてみましょう。これは冪等な操作ではありません。もしステップが100ドルを加算し、失敗し、再実行され、再び100ドルを加算した場合、合計200ドルが口座に追加されることになり、これは正しくありません。最も一般的な解決策は、これを防ぐためのアプリケーションレベルのブックキーピングを追加することです。例えば、どの支払いが適用されたかを追跡するために追加のapplied_paymentsテーブルを追加し、それをトランザクションで更新し、口座に二重に加算されないようにチェックすることができます。ワークフローの状態とアプリケーションデータが同じPostgresデータベース内に共置されている場合、この複雑さの多くを排除できます。データベーストランザクションがコミットされた後にステップをチェックポイントする代わりに、共置されたワークフローエンジンは、同じトランザクション内でステップのチェックポイントを書き込み、データベースの更新を実行できます。これを行うために、ワークフローはワークフローエンジンによって提供されるデータベーストランザクションを使用してステップを実行します。ステップはそのデータベース更新を実行し、ワークフローエンジンはチェックポイントを記録し、トランザクション全体がアトミックにコミットされます。データベース更新とチェックポイントの書き込みを同じトランザクションの一部にすることで、ワークフローエンジンはトランザクションステップに対してexactly-once実行セマンティクスを提供できます。トランザクションがコミットされた場合、データベース更新とチェックポイントの両方が耐久性をもって記録され、ステップが二度と実行されないことが保証されます。コミット前に何らかの障害が発生した場合、データベース更新とチェックポイントの両方を含むトランザクション全体がロールバックされます。ワークフローが復旧すると、ステップは安全に最初から再実行されます。これにより、対応するチェックポイントなしにデータベース更新が成功する可能性のあるウィンドウがなくなります。その結果、トランザクションステップはアプリケーションレベルの冪等性ロジックやブックキーピングテーブルを必要としなくなります。データベース操作は、一度だけ実行されてチェックポイントされるか、全く実行されないかのどちらかです。トランザクションワークフローアウトボックスによるアトミック性分散システムにおけるもう一つの古典的な課題は、複数のシステムで信頼性の高い更新を実行することです。例えば、データベースレコードを更新し、別のシステムに通知を送信することです。これは、操作がアトミックである必要があるため、想定よりもトリッキーです。つまり、障害(プロセス障害やネットワークグリッチなど)が発生した場合でも、両方が実行されるか、どちらも実行されないかのどちらかです。例えば、顧客が新しい注文を送信するたびに、倉庫に注文を送信するワークフローを開始したい場合があります。アトミック性がない場合、データベースとダウンストリームシステムは不整合になる可能性があります。注文がコミットされずに送信されるか、コミットされていない注文について倉庫に通知される可能性があります。この問題に対する最も一般的な解決策は、トランザクションアウトボックスです。その考え方は、データベースに新しい「アウトボックス」テーブルを維持することです。アトミックな更新を実行する必要がある場合、単一のデータベーストランザクションを実行します。これは以下の両方を行います:データベースレコードを更新する「アウトボックス」テーブルにメッセージを書き込む別個のバックグラウンドプロセスが、その後アウトボックステーブルをポーリングし、そこにあるメッセージをターゲットシステムに配信します。データベースレコードの更新と「アウトボックス」へのメッセージの書き込みを1つのトランザクションで実行することは、アトミック性を保証します。つまり、両方のレコードが更新されるか、どちらも更新されないかのどちらかです。メッセージがアウトボックスに書き込まれると、トランザクションコミット後に障害が発生した場合でも、非同期に配信できます。トランザクションアウトボックスは広く使用されていますが、運用上の複雑さが増します。アウトボックスをポーリングし、メッセージを配信し、再試行を処理し、障害を監視するためのインフラストラクチャが必要です。ワークフローエンジンが別のシステムである場合、データベースと同期がずれる可能性があります。実際には、不整合を解決するには、通知をダウンストリームシステムに送信せずにデータベースレコードが更新されたことを検出するための調整ジョブのような追加のインフラストラクチャが必要です。データベースバックのワークフローを活用し、ワークフローの状態をアプリケーションデータと共置することで、このパターンを簡素化できます。手動でアウトボックステーブルと別個のポーリングプロセスを維持する代わりに、Postgresユーザー定義関数(UDF)を使用して、アプリケーション更新と同じデータベーストランザクション内でワークフローをキューに入れます。これはトランザクションアウトボックスと同じ原則に従って機能します。ワークフローは、その名前、キュー、および入力を格納するデータベース行として表されます。enqueue_workflow UDFは、ユーザーデータベース更新と同じトランザクション内でこの行を作成し、アトミック性を保証します。つまり、更新が完了してワークフローがエンキューされるか、どちらも発生しません。その後、ワーカーが非同期にワークフローをデキューして実行し、必要な操作を確実に実行します。詳細はこちらスケーラブルで信頼性の高いシステムを構築するのが好きなら、ぜひお話ししましょう。DBOSでは、Postgresバックの耐久性のある実行を可能な限りシンプルかつ高性能にすることを目指しています。ぜひチェックしてみてください:クイックスタート:https://docs.dbos.dev/quickstart GitHub:https://github.com/dbos-inc Discordコミュニティ:https://discord.gg/eMUHrvbu67 数週間前、私たちは「単にPostgresを使え」と耐久性のあるワークフローについて書きました。その投稿は多くの議論を呼びましたが、誤解も生みました。私たちは、ワークフローエンジンがPostgresに状態を保存することを意味しただけではありません。ワークフローシステムは、アプリケーションと同じPostgresデータベース内に存在でき、しばしばそうすべきだと私たちは考えています。一見すると、これは良い考えではないように思えます。それらの懸念は分離されるべきではないでしょうか?ワークフローの状態は一つのデータベースに、アプリケーションデータは別のデータベースに置かれるべきではないでしょうか?そうではないかもしれません。分散システムにおいて、共置はスーパーパワーです。ワークフローメタデータとアプリケーションデータが同じPostgresデータベース内に存在する場合、それらは同じデータベーストランザクションで更新できます。これは部分的な障害を不可能にし、すべてのエッジケースを正しく処理するワークフローを構築することをはるかに容易にします。この記事では、それがなぜ可能であるか、そしてトランザクションがいかに冪等性や原子性のような難しい問題を簡素化できるかを説明します。トランザクションステップによる冪等性分散システムにおける基本的な課題の一つは、特にデータベース状態を変更する操作において、冪等性です。耐久性のあるワークフローは、各ステップが完了した結果をチェックポイントとして保存することで耐障害性を実現します。ワークフローが中断された場合、最初からではなく、最後にチェックポイントされたステップから再開します。しかし、ワークフローはステップを完了した後、チェックポイントを記録する前に中断される可能性があります。復旧時、そのステップが既に実行された記録がないため、再度実行してしまいます。その結果、耐久性のあるワークフローだけでは冪等性の問題は解決されません。ワークフローエンジンは通常、重複した副作用なしに安全に再試行できるように、ステップが冪等であることを要求します。例えば、銀行口座に(お金を)加算するステップを考えてみましょう。これは冪等な操作ではありません。もしステップが100ドルを加算し、失敗し、再実行され、再び100ドルを加算した場合、合計200ドルが口座に追加されることになり、これは正しくありません。