Flawless - Rust向けの耐久実行エンジン
(flawless.dev)- Flawlessは、障害が発生してもコードが最後まで実行されるよう支援する耐久性のある計算実行エンジン
- ワークフローは通常のRust関数として記述するが、ネイティブコードではなくWebAssemblyにコンパイルして決定論的な環境で実行する
- HTTPリクエストや乱数生成のように外部世界と接する地点でのみ非決定的な副作用が発生し、Flawlessはこれをログとして保存する
- 実行が中断されると、保存されたログを使って同じ状態まで再追従し、すでに実行済みの副作用は繰り返さない
- 開発者はデータベースにすべての状態を直接モデリングせず、コードとローカル変数で永続状態を表現できるため、サーバー再起動後も中断地点から継続できる
コードで永続状態を表現する実行モデル
- Flawlessは、ハードウェアやソフトウェアの障害が発生してもコードが完了するまで実行されるようにする耐久性のある計算エンジン
- 複雑なユーザー体験には複雑なUIと状態が必要だが、すべての状態をデータベース内に直接モデリングするのは難しい
- ユーザーが誤ってページを再読み込みしても進行状況を失わないようにするには、現代のアプリケーションには永続ストレージが必要
- Flawlessは、永続状態をコードとローカル変数でモデリングできるようにし、アプリケーションの複雑な動作をより簡単に表現することを目指している
WebAssemblyベースの実行と障害復旧
- ワークフローは通常のRust関数として記述され、任意のロジックを含められる
- 関数はネイティブコードの代わりにWebAssemblyへコンパイルされ、完全に決定論的な環境で実行される
- HTTPリクエストの実行や乱数生成のように外部世界と相互作用するときにのみ非決定性が導入される
- Flawlessは、このような非決定的な副作用ログを保存して復旧に利用する
- ワークフローの実行が中断された場合、再実行して同じ状態まで追いつく
- すでに実行された副作用は再実行しない
- 保存すべきデータ量は最小化し、それ以外は障害発生時に必要に応じて再計算する
- この実行モデルにより、システム全体の動作をよりよく観測できる
- 完了したワークフローや実行中のワークフローの正確な実行経路を分析できる
- 決定論的な実行環境により、再現が難しいバグも扱いやすくなる
- 開発者は状態保存の負担を減らし、ビジネスロジックの記述により集中できる
- メンテナンスのためにサーバーを再起動する必要があっても、Flawlessエンジンを再起動すればワークフローは停止地点から継続して実行される
- 2024年12月9日時点でFlawless Beta 3が提供されている
1件のコメント
Hacker News の意見
ワークフローのバージョン管理をどう扱うのか気になる。Temporal/Cadence のようなシステムで最も難しい問題だと思う
新しいコードで既存の副作用ログ記録を正確に再生できる場合にだけ、アップグレードは成功します。新しいコードで追いつき、追いついたらそのまま実行を続ける方式です
新しいコードが既存の記録と食い違うと、アップグレードは失敗し、以前のコードに戻ります。この場合は人が介入して、何が間違っていたのか確認する必要があります
他のアプローチもありますが、実際に理解して使うにはこの方式が最も単純だと思います。開発中にも既存ログを使って、コードが分岐するかどうかをテストできます
複数バージョンを同時に維持したいのか、それとも進行中のものを最新のワークフロー定義へ移行する方法が必要なのかも気になる
私たちの分野は、いまや建築や医学にますます近づいている感じがする。こうした技術のおかげで、いじって試す段階を抜け出し、本格的な工学文化へ入っていける
短く聞くと、こういうシステムで DoS 攻撃の影響が永続化されるのはどう防ぐのだろう
建築は規制上の制約の中で主観的な意見を扱うものに近く、医学は実験で検証される経験的知識だ。どちらも工学に似ているとは言い難い
建築は芸術の一形態に近く、大学の建築学科も普通は芸術系の学部にある
医学は工学のように応用科学ではあるが、工学分野そのものではない
この決定性が浮動小数点計算にまで拡張されるのか気になる
マルチプレイヤーゲームでは、クライアント状態が浮動小数点計算で少しずつドリフトを蓄積するため、サーバー状態と定期的に再同期する必要があり、歴史的にかなりつらいポイントだった
https://webassembly.org/docs/faq/#why-is-there-no-fast-math-... と https://github.com/WebAssembly/design/blob/main/Nondetermini... を参照
https://asawicki.info/news_1741_myths_about_floating-point_n...
以前は CPU の結果も実行経路によって変わることがあった。記事中のツイートを参照
f64の代わりに大きな数値構造体を同期するのも選択肢になり得るのか、それとも複雑さとサイズの面で現実的ではないのか気になる副作用の状態はどこに保存されるのか気になる。例えば冪等にしたい AWS Lambda があるとすると、Lambda には実行間で維持されるローカルストレージがない
EBS ボリュームのようなものをマウントしない限り状態は維持されないので、状態は DB に保存できると考えればいいのだろうか?
中核となる原理と動作を見せるアニメーションが気に入った。本当によくできている
コードはあまりきれいではありませんが、実装は直感的なので、確認してみたいならここにあります: https://flawless.dev/js/how-does-it-work-animation.js
面白そうだが、関数を副作用ありとしてマークする方式が、ミスなく作りやすいのか気になる
例で乱数生成が副作用なのは、flawless が提供する乱数生成器から来ているからだと仮定している。通常の Rust 関数でも可能だったのだろうか?
開発者がワークフローを確認できるテストハーネスのようなものもあるのだと思う
WebAssembly は、モジュール内でホスト呼び出しを明示的に宣言することを要求します。flawless が提供していない他のホスト呼び出しを使おうとすると、モジュールをインスタンス化できません
WebAssembly エコシステムでは、いくつもの標準化作業も進んでいます。例えば Rust の
randクレートを使って WebAssembly にコンパイルすると、乱数生成のために WASI ホスト関数を使いますwasi、wasi-httpなどが標準化されるのを待つ間は、ひとまず独自のインターフェースを公開していますもちろん大きな欠点もあります。すべての Rust コードを WebAssembly にコンパイルできるわけではありません。それでも、意図しない副作用が絶対に発生しないことを保証するデフォルト拒否方式のほうがよいと思います
flawless名前空間配下にあるのを見ると、Rust エコシステム全体へアクセスさせる代わりにstd::flawlessのようなものを使わせることになりそうだハーネスが大半の問題を解決してくれるだろうが、どれだけ多くの機能をマッピングできるのかは気になる
今のところは、Rust を使うスクリプティングランタイムにより近く見える
WASMをランタイムとして使うTemporalのRust代替のように見える。気に入った。
windmill.devの創業者だが、私たちもRust製の耐久性エンジンを扱っている。ただし、こちらはずっと洗練されていない。ワークフローをPython/TypeScript/Go/Bashの明確なステップに分け、未完了のステップから再開するには最後のステップからやり直し、各ステップの結果をPostgres DBの
jsonbに永続保存する。ユースケースは明らかに違うし、flawlessはサイトで述べられているように非常に軽量なので、UIフローの状態をモデル化し、小さなサーバーで数百万件までスケールさせる用途にも使えそうだ。
素晴らしい。いつかRustがすべての分散システムを動かすようになってほしい。
「他の言語で書かれた十分に複雑な並行プログラムは、その場しのぎの非公式仕様による、バグが多く遅いErlangの半端な実装を含む」— Virdingのプログラミング第一法則
これはErlangとはかなり違う。Erlangは主に、ハードウェア問題の多いプロトタイプ機器しかない状況でもソフトウェアを開発するために作られた。
私には、この2つのアプローチは正反対に見える。Flawlessは途中でクラッシュするワークフローを完了させようとしてループにはまり得るが、Erlangはハードウェアバグを踏んだトラフィックの50%を平気で捨てられる。
Erlangは永続状態を実質的になくす方法で解決する。ほぼすべての状態はメッセージキューや外部DBのような場所に存在する。
Flawlessはファイルシステムのジャーナリングに似ているが、完全に同じではない手法で問題を解いているようだ。副作用を実行するときに記録しておく方式だ。
ファイルシステムのジャーナリングはクラッシュ後に再実行するためのものだが、ここでは再実行する必要をなくすためのものだ。
どの領域にどれだけ合うかは明確ではないが、Erlangが適している領域との重なりは完全ではなさそうだ。
とても素晴らしい。AmbientというWASMゲームランタイムでも似た問題がある。競合するプロセスがあり、相互作用を再試行しなければならない可能性があるので、ここで示されているアプローチは興味深い。
ところでLunaticとの関係が気になる。Lunaticも引き続き開発中なのか、これはサイドプロジェクトなのか、それとも完全に別物なのか?
https://lunatic.solutions/
https://kolobara.com
「任意の計算を開始すると、システムが完了するまで実行され、すべての作業が正確に一度だけ実行されると保証される、と想像してみてほしい」
これがどう保証されるのか気になる。分散システムで正確に一度の配信は不可能ではないのか?
システムが進行できるなら、メッセージは正確に一度だけ配信される。
存在証明が必要なら、NFSv3が1980年代にすでにこれを動かしていた。最初だったかどうかは知らない。