ディスカッション (11件)
Postgresを活用して、障害に強く、永続性の高いワークフローを構築するための手法について解説します。複雑な分散システムにおいても、信頼性の高い状態管理を実現するための設計パターンやベストプラクティスを網羅しています。
Postgresさえツールキットの一部にあれば、少ないツールでここまでできるのかといつも驚かされるよ。
最近、分散キューを自作してみたんだけど、かなりいい感じで動いてる。レースコンディションや競合もなくて、ベンチマーク結果も良好。ワーカーが安全に競合できるように SKIP LOCKED を使ったのがポイントかな。
あと、ノードをまたいだ複数のワーカー間での競合回避なら、セッション単位のミューテックス、つまり pg advisory lock を使う手もあるよ。
DBOSとTemporalの両方を使ってる人の感想が聞きたいな。
以前Temporalを使っていた時はすごく便利だったけど、リクエストペイロードやイベントサイズに制限があって、ソリューションを組む時にちょっと困ることがあった。堅実なエンジニアリングプラクティスを強制してくれるのは良いんだけど、CSVファイルが2MBを超えるたびに「一度S3に上げてリンクを渡して、ワークフロー内でダウンロードする」みたいな特殊なロジックを書かなきゃいけないのは正直面倒だったよ。
DBOSはどう?運用の複雑さや機能の網羅性なんかでTemporalと比べてどんな感じ?
DBOSはRustに対応していないから、私たちはごく最小限のRust版を実装してみたよ。https://github.com/tensorzero/durable (https://github.com/tensorzero/durable) にある。かなり安定していて拡張性もあるけど、当然ながらSQLの実装にはめちゃくちゃ気を使う必要があるね。皆の参考になれば幸い。
こういうアーキテクチャって、最初はシンプルだけど、だんだんワークフローネイティブなシステムが持っている機能を全部自前で実装していくことになるよね。これまで似たようなシステムをいくつも見てきたし、このアイデアで会社が立ち上がるところも、実際に小規模なシステムをコツコツ作る現場も見てきたけどさ。
リトライ、バックオフ、タイムアウト、キャンセル、バージョニング、可視化、タスクルーティング、レート制限、リース、ハートビート、スタックしたワーカーの検知、リプレイ・デバッグ、ワークフロー移行、ファンアウト/ファンイン、ロングタイマー、監査ログ、運用ツール...これらが必要になった途端、「データベースを使うだけ」という話は「ワークフローエンジンと大量のワーカーを自前で実装する」という地獄にすぐ変わるんだよね。
もちろん、Postgresがすでに核となる運用依存先なら、それはそれでアリなトレードオフだと思う。ただ、「データベース vs 過剰に複雑なオーケストレーター」という対比ではなく、「どの程度の複雑さを自分で抱え込み、どの程度をプロ向けのシステムに任せる(外注する)」かという話にするべきだと思うよ。
Conductor OSSならそのあたりを結構うまくやってくれるよ。https://docs.conductor-oss.org/devguide/ai/index.html (https://docs.conductor-oss.org/devguide/ai/index.html)
あと https://github.com/agentspan-ai/agentspan (https://github.com/agentspan-ai/agentspan) はConductor用のエージェント指向SDKみたいなもので、langgraphやOpenAI、Vercel、ADKのエージェントをコード変更なしで耐久性を持たせてオーケストレーションできるようにしてくれる。
Armin Ronacherによる absurd は、Postgres向けの耐久ワークフロー実装だね。
https://lucumr.pocoo.org/2025/11/3/absurd-workflows/ (https://lucumr.pocoo.org/2025/11/3/absurd-workflows/)
https://github.com/earendil-works/absurd (https://github.com/earendil-works/absurd)
https://earendil-works.github.io/absurd/ (https://earendil-works.github.io/absurd/)
自分では使ったことないけど、他の選択肢と比較する価値はあるはず。
データストレージ、ステートマシン、状態遷移のルール、そして状態を遷移させるロジックを切り分けるんじゃなくて、アプリ状態の「カーネル」みたいなものに統合したいっていうのが僕の夢なんだ。ぶっちゃけPostgresはすでにその機能の多くを持ってるんだけど、アプリやプロダクトレベルで「正しく遷移可能な状態」を保証して、それをクライアントにわかりやすく提示する(例:「このユーザーはこの投稿にいいねできるけど、編集はできない」)っていうストーリーがまだ明確に見えてこない。彩色ペトリネットみたいな形に見えるんだけど、データベースにあるような成功した境界線に似た、シンプルなアプリ状態のパラダイムがまだ見つかっていないんだよね。
概念はすごくよくわかるし同意する。ワークフローシステムに耐久性を持たせるには最高のやり方だよね。
ただ、ゲーマー脳の自分からすると、これは「スケールしたセーブスカミング(やり直し)」って呼びたくなるな。多くの人がこの手法がうまくいくことは知っていても、それを抽象的なCS(計算機科学)の概念と結びつけられていないだけかもしれない。
堅牢性を高めるもう一つの戦略として、ワークフローを「冪等(べきとう)な操作」だけで構成するというのがある。ワークフローの状態が大きすぎてバックアップできない場合に便利なんだ。代わりにジョブの最初から再実行すれば、進捗するまでは何も起きない(noop)状態だから安全というわけ。
TypeScriptで書いて、複数のドライバー(PostgreSQL, Firestore, SQLite3, ファイル, Redis, インメモリ)に対応した実装を使っているよ。小規模な用途には十分すぎるほど機能している。必要なら後から専用のキューイングシステムに乗り換えられるようなインターフェースにもしているんだ。
パイプラインやバッチ処理、基本的なランナー、あと冪等キー(バッチング含む)もサポートしてる。キューを複数のサブキューに「パーティション分割」できるから、外側の設定をいじらなくてもアプリケーション内で簡単にジョブを隔離できる。例えば、PostgreSQLと通信するルートキューを作って、それをサブシステムに渡せば、各サブシステムが独自のサブキューを作成してエンキューやデキューを行えるようになる仕組みだ。
今は社内用だけど、ドキュメントを整えてパッケージとして公開しようかと思ってる。フィードバックやプルリクは大歓迎!
[0] https://github.com/KeetaNetwork/anchor/blob/main/src/lib/que... (https://github.com/KeetaNetwork/anchor/blob/main/src/lib/queue/index.ts)
[1] https://github.com/KeetaNetwork/anchor/blob/main/src/lib/que... (https://github.com/KeetaNetwork/anchor/blob/main/src/lib/queue/pipeline.ts)
DBOSのワークフローエンジンが、スケーリングやリカバリのために(Conductorのような)有料コンポーネントに依存する設計なのはナシだな。RiverにもDLQをサポートしないといった「罠」があるし、結局そこは有料機能っていうパターンが多いよね。