- Antithesisは、FoundationDBで成果を上げた決定論的な自律テストを一般的なソフトウェアにも適用し、再現が難しい分散システムのバグを反復可能な問題に変えようとしている
- FoundationDBはデータベース実装の前に単一スレッド・単一プロセスのシミュレーションを作り、同じランダムシードでまれな障害状況を再実行できた
- この方式により、並行性、ネットワーク遅延・再順序化、ディスク障害、マシン障害といった非決定的な失敗をテスト対象にでき、FoundationDBでは顧客報告バグが全期間で1〜2件しかなかったと評価されている
- Antithesisは既存ソフトウェアを最初から書き直させないため、決定論的コンピュータをエミュレートするハイパーバイザーを作り、現在は分散システムの信頼性・耐障害性テストに注力している
- MongoDB、Ethereum Foundation、Palantirと協業し、まれなバグを見つけるツールから最新ビルドを継続的に検証する常時テストサービスへと進化している
FoundationDBでの経験から始まったAntithesis
- Antithesisは5年以上にわたるステルス開発の後、FoundationDBで得た決定論的テストの経験を土台にしたプラットフォームを公開した
- 公開前には採用、初期顧客、投資家との取り組みを進めており、最初に明らかにした中核は複雑なシステムを反復可能にテストする方法だった
分散データベースで最も難しかった検証問題
- FoundationDBは2010年、ACIDトランザクションをサポートするスケーラブルで耐障害性のある分散データベースの開発を始めた
- 当時はSpannerが公開される前で、CAP定理を強い一貫性と高可用性は両立不可能だと誤解する人も多かった
- 最大の難点はデータベースそのものより、そのようなシステムをどうテストし、正しさへの確信をどう得るかだった
既存テストが見落とす「未知の未知」
- ソフトウェアは開発者が事前に思いつかなかった状況にも対処しなければならないが、一般的なテストは既に想定したケースの検証には強い
- 特定のケースをテストとして使えるほど予見していたなら、コードもその状況を処理するよう書かれていた可能性が高い
- そのため既存のテストは回帰防止には有用だが、実際のユーザーや運用環境が生み出す想定外の障害を捉えるのは苦手だ
- 分散ストレージシステムではこの問題がさらに大きくなる
- マシン内部とマシン間の並行性が同時に存在する
- ネットワークは遅延やパケットの再順序化を引き起こしうる
- ディスク障害、マシン障害、停電、データセンター火災、人為ミスまで失敗要因が広い
- 致命的なバグが複数マシンにまたがる事象の順序に敏感だと、一度見つけても再現が難しい
FoundationDBの決定論的シミュレーション
- FoundationDBチームは、データベースを書く前に完全に決定論的なイベント駆動型ネットワークシミュレーションを先に作った
- クラスタ全体を単一スレッド・単一プロセスのアプリケーション内でシミュレートし、同じ乱数生成器で実行を駆動した
- 仮想クラスタでネットワーク障害を注入し、マシンを停止させ、さまざまな異常状況を繰り返し作り出せた
- 特定の実行でアプリケーションロジックのバグが見つかれば、同じランダムシードで同じ事象の順序を再実行できた
- そのおかげで非常にまれなバグでも、ログ追加やデバッグ手順の反復を通じて追跡可能になった
- 関連発表は2014年のStrangeloopで行われ、動画はこちらで見られる
テストが開発速度を変えた方法
- FoundationDBでは、会社の全歴史を通じて顧客が報告したバグは1〜2件しかなかったと評価されている
- Kyle Kingsbury、つまり“aphyr”はJepsenでFoundationDBをテストしても見つかるものがないとして、テストしなかった
- テストが新しいバグをすぐに露呈させる状態になると、チームのプログラミングのやり方も変わった
- コンパイラや強力な型システムは特定種のバグに対する確信を与えるが、数千もの予想外の状況で実際のソフトウェアを動かすこととは別物だ
- この信頼を土台に、FoundationDBチームは大きな変更を断行した
- Zookeeperを含むすべての依存関係を取り除き、自前のPaxos実装を短期間で書き上げ、その実装はFoundationDB論文に含まれている
- トランザクション処理サブシステム全体を、より高速でスケーラブルになるよう再実装した
- 最大の効果はデータベースの安定性向上だけではなく、小規模なエンジニアリングチームに50倍規模のチームの生産性を与えた点だった
Apple買収後に見えた空白
- Appleは2015年にFoundationDBを買収し、Appleの“cloud infrastructure”の基盤として利用した
- 数年後、FoundationDBはオープンソースとして公開された
- FoundationDBのメンバーが他の大手テック企業へ散っていった後も、そのような組織にはFoundationDB式の決定論的シミュレーションテストがなかった
- 意図しないシステム影響を予測しにくいため、バックエンドシステムの変更は遅く進み、本番バグの診断と修正には上級エンジニアの時間が何カ月も費やされた
- 2018年にDave SchererとともにAntithesisを立ち上げ、目標はFoundationDB式の決定論的自律テストを他チームにも提供することだった
既存ソフトウェアを決定論的にする方法
- FoundationDBは最初からこの方式でテストすることを前提にしたグリーンフィールドプロジェクトで、依存関係も排除できた
- 一般的なソフトウェアはスレッドを作り、時間を確認し、カーネルから乱数を要求し、ネットワークで他のソフトウェアと通信する
- すべてのソフトウェアを最初から書き直さなければならない開発手法は広く使われにくいため、Antithesisは決定論的なコンピュータをエミュレートするハイパーバイザーを書いた
- その結果、ハイパーバイザー内で動くソフトウェアを決定論的実行環境に置けるようになった
- この過程にはIntel CPUのextended page tableのような低レベル動作を扱う作業も含まれる
- 任意プログラムの状態空間で性質違反を見つける問題は停止問題よりさらに難しく、すべてのプログラムに対する停止オラクルがあっても計算不可能なテスト特性が存在しうる
現在のプラットフォームと顧客事例
- Antithesisプラットフォームは、ユーザーのソフトウェアを受け取りバグを見つけ、見つけたバグを常に再現可能にすることを目指している
- 複数のサービスがネットワークで通信する複雑なケースでも再現性を維持しようとしている
- バグを見つけた後は強力なデバッグ機能を適用できる
- 長期的にはさまざまなソフトウェアの多様な種類のバグを見つけるよう設計されているが、現在は既に経験のある分散システムの信頼性・耐障害性テストに集中している
- ここ数年、信頼性が重要な大規模で複雑なシステムを運用するエンジニアリングチームと協力してきた
- MongoDBとは数年にわたり協力し、core server softwareとWiredTiger storage engineのテストを支援
- Ethereum FoundationとはMergeの約1年前から協力し、Mergeテストを支援、現在も協業中
- Palantirとも協業中
まれなバグ向けツールから常時テストサービスへ
- 初期顧客はAntithesisを、最も見つけにくく危険なバグを発見し再現するための特殊部隊ツールのように使っていた
- プラットフォームがより成熟し対話的になるにつれ、最新ビルドを継続的にテストする常時サービスへと転換した
- 目標は、バグが導入された時点から発見される時点までの時間を短縮することだ
- FoundationDB開発当時、この方式はバグの診断と修正をはるかに容易にし、効率性とソフトウェア品質を高めた
- Antithesisは、分散システムを運用し信頼性とエンジニアリング生産性を重視する組織との対話を望んでいる
- 難しい問題に取り組みたい人には採用情報を案内している
1件のコメント
Hacker News の意見
「伝説的な 10x 開発者」という言葉が、週6.5日、1日15時間働いて燃え尽きる人を指すように歪められている感じがする
本当の 10x、あるいは 50x の生産性は、ほとんど誰も可能だと思っていなかったり理解していなかったものを実装し、動くソフトウェアをはるかに短い時間で作れるようにする人から生まれる
マネージャーは、8時間分の仕事を12時間かけてやる人を、同じ仕事を8時間で終える人よりも注目しがちなことがあまりに多い
また「普通」から外れる試みは好意的に見られず、プロセスを改善する時間もスケジュールに入っていないため、バケツをもっと速く運べばいいと考えられている状況で手押し車を作るようなことは抑え込まれる
だから 10x エンジニアは存在する。30歳になる頃には、10年ではなくおよそ20年のプログラミング経験を持っている
職業経験もずっと多い。15歳のときは親戚の仕事をときどき手伝うような変わった案件から始めるとしても、18歳ごろには専門の会社に入り、コンピュータサイエンスの勉強と並行して働く
少なくとも昔はそうだった。2004年から2018年ごろまでは現実だったが、最近の採用環境でもまだ可能なのかは分からない
10x エンジニアが存在するには、いくつかの例があれば十分だ。まれな存在だという点にはおおむね皆同意しているようで、公開されている 10x エンジニアの例としてこの人を挙げられる。本人は絶対にそうは言わないだろうが、私の推測では 10x エンジニアだ https://bellard.org/
同意しないなら、どこが違うのか知りたい。私は 群盲象を評す のように一部だけを触っている人間にすぎず、全体像を見ているとは主張しない
すべてを一人でこなす 一人軍隊型の開発者は、仕事が標準化され、細かく分けられて分散されるチームにはあまり合わない
そういう人たちは、邪魔をする同僚やマネージャーなしに自分のプロジェクトを進めるときに最も力を発揮するが、ほとんどの職場はそうではない
チームの一員になると、どれほど優秀でも一人であまりに多くの仕事はできず、結局はより遅い、あるいは力の弱いチームメンバーが作った問題や管理側の問題に対処するために速度が落ちる。だからチームはロックスターがいても、最小公分母の速度で動くことになる
内部ツールを作っていて、プロセスやステークホルダーに非常に近いことも助けになっている
「ふむ、これを達成する別の方法があるな」が 10x に相当するのであって、より速くやることが本質ではない
これまで読んだ紹介文の中で最高かもしれない
人々が誰で、何を作ってきたのかという土台をうまく敷き、今作っているものが以前に作ったものの結果であることを説明している
この問題を皆のために解決したいのだという感じが伝わってくるが、それはすでにそのソリューションがどれほど良いかを自分たちで体験しているからのように見える
続いて、すでに使っているチームも示しており、複雑なシステムを持つかなり大きな名前が並んでいる
これらすべてが、開発者や創業者に響く良い文章としてまとまっていて、ランディングページも素晴らしい
実際のユースケースや例をもう少し見たかった
代わりにいくつかの大企業名を並べ、魔法のように動く革新的プロダクトだと主張したうえで、「10x プログラマー」「ステルスモード」のような典型的なバズワードを入れている。顧客名を公開しながらステルスモードと言うのはつじつまが合わない
これまで経験したことのない生き方、考え方、実行の仕方を提供してくれるので、そのソリューションが欲しくなる
リンク先の記事は、実際に何を作ったのかを語るまでの 3/4 が歴史と根拠の話だ
ヴィーガンパンケーキを作りたいのに、著者の子どもの頃の話から延々と始まる面倒なレシピブログみたいだ
すばらしいピッチだし、否定的に見られたいわけではありませんが、「すべてのバグを見つけた」のような文は、バグの定義をかなり狭く取らない限り真とは言えない気がします
これまで経験した中で最も悪質で見つけにくいバグは、エラー状態に陥ることよりも、アプリケーションのビジネスロジック周辺にありました
たとえば、データベースには顧客の完了済み取引が記録されているのに、完了済みの購入項目がない場合、顧客の最近の取引ページにどう表示すべきか、というような問題です
そのような場合、「何かが表示され、クラッシュしない」ことを実装するのと、スタック全体で他の選択との文脈上、実際に筋の通った選択であることを保証するのとは、まったく別物です
データベースなら、「クエリプランナーがこの境界ケースで非常に非効率なプランを作る」といった問題もあります
この種のものは自動検出できません。プログラムがエラー状態に到達する問題ではなく、そもそもアプリケーションにおける「正しさ」とは何かを把握する問題だからです
私がバグの基準を高く置きすぎているのかもしれませんが、バグ 0 を想像することと、現実世界でソフトウェアを作ることは違います。それでも ランタイムエラー 0 件 なら受け入れられそうです
ただし、FoundationDB がテスト慣行の最先端を押し上げたことで本当に有名なのは事実です: https://apple.github.io/foundationdb/testing.html
普通なら傲慢さや過信のにおいがしますが、ここでは実際にバグ 0 に非常に近いところまで到達していました
もちろん否定命題を証明することはできませんが、そのような「全部グリーン」の状態に到達したという事実は、堅固な基盤の上に積み上げているという大きな確信を与えてくれましたし、時間がたつにつれて実際にそうだったことが明らかになりました
ビジネスロジック周辺の問題はシステムの失敗ではなく、システムは仕様どおりに動作し、仕様が十分に包括的ではなかったので、あとは反復改善すればよい、ということです
もちろん、多くのソフトウェアにはドキュメントが不足していて、それはドキュメントのバグです
それでもこの定義が優れているのは、ドキュメントが不完全でも「本当にこの動作を文書化するのか、それとも動作を変えてそれを文書化するのか?」と問わせるからです
少なくとも私にとっては、奇妙な動作を適当に覆い隠して済ませるのが難しくなります
sledのシミュレーションガイド https://sled.rs/simulation.html でこの分野を知って以来、ものすごく興味を持つようになりました。その記事は FoundationDB がどのようにやっているかを概略で示しています今は職場で似たようなテスト方式を導入しようとして、サービスが
madsimhttps://github.com/madsim-rs/madsim?tab=readme-ov-file#madsim の上で動くように書いていますこうすると、tokio で async/await スタイルのサービス作成を続けながら、テストでは OS を呼び出す依存関係まで含め、あらゆる非決定性の源をパッチした決定論的実行器に置き換えられます。かなり滑らかに動きます
この記事の著者が初期コストは莫大だと言ったのは誇張ではありません。考え得るあらゆる非決定性の源を処理し、サービスをテスト可能で sans-IO https://sans-io.readthedocs.io/ な形に書き直すには、多くのエンジニアリング労力がかかります
しかしシステムが整うと、コードに対して感じる確信は言葉で説明しにくいものがあります。quickcheck https://github.com/BurntSushi/quickcheck?tab=readme-ov-file#quickcheck のようなツールと組み合わせれば、入出力、イベント順序、タイムアウト、パケットロス、ファイルシステム障害など、何十万もの微妙な失敗ケースをテストできます
このようなテストは、投資する忍耐と粘り強さがあるなら、ツールボックスに入れておける非常に強力な道具です
Antithesis 自体もとても格好よく見えます。決定論的テストを OS より下の層まで下げるのはすごいことですし、毎回手作業でハーネスを組まなくてもシステム全体をテストできるようにしてくれそうです。早く使ってみたいです
そのようなシステムで見てきた複雑さのかなりの部分は、「関数」呼び出しが非同期で、OS に依存し、いつか実行されるか、まったく実行されない可能性があり、静的型システムに戻すためにパースしなければならない文字列の束を返し、それ自体の失敗モードを持つ、という点から来ています
ロジックを名前付きの構成要素、つまり関数として抽象化するという、一見単純なことが極度に複雑になります
ロジックを同じプロセス内に置いて関数を呼ぶだけなら、述べたような微妙な失敗をテストする必要はありません
モノリスが常に良い選択、あるいは正しい選択というわけではありませんが、現在のサービスベースのソフトウェアアーキテクチャの流行が正当で、それに見合う見返りがあるのかについては、非常に懐疑的です
また、Rust を使う企業がこのような方式で開発しているのかも気になります
付け加えると、TigerBeetle もこの方式で書かれた製品です
madsimや決定論的シミュレーションテストのようなものがあるのか気になります記事が本当に面白い
「この状態でプログラミングするのは、あらゆる害から守ってくれる力場に囲まれて暮らすようなものだ……バグがあったので、Zookeeperを含むすべての依存関係を削除し、ごく短期間でPaxos実装を自分たちで書いたが、それにはバグがなかった」という文を言えて、証拠で裏付けられるなら本当にすごいことだと思う
その本では、数値ソフトウェアパッケージのバグのせいで自分の問題を解いている最中に他人のソフトウェアをデバッグしなければならないのがあまりに歯がゆく、線形代数パッケージを除けば普通は自作すると述べている
さらに厄介なのは、パッケージが問題の定式化の欠陥を隠してしまう点だと見ている。方程式の集合をソルバーに入れると、条件が悪かったり予期しない特異点があって物理的現実と答えがずれていても、たいてい文句を言わずに解を返し、大きなプログラムの中に埋もれていると、そうした可能性を無視させてしまうことがある
怪しい挙動を見つけても、パッケージの中に入り込んで問題を掘り下げるのは難しいため、結局は自分で再実装することになり、最初からそうしていれば問題の現実に深く入り込み、論理的な混乱を事前に取り除けた可能性が高いという
結局そうするかどうかは、自分がどれだけ厳密か、特定の依存関係がどれだけ厳密か、時間がどれだけあるかに左右される。自分でデータベースを書くことはないだろうが、それはあまりに複雑で、十分にテストされた選択肢が多いからだ。一方で、テストが弱い小さなパッケージの一部機能だけを使うなら、自作するのは理にかなう場合がある
ただし、私の仕様にバグがないとは主張しない
3つのことを考えた
第一に、適切なタイミングで出てきた優れたアイデアだ。ファザー、静的型付け、メモリ安全性、標準化されたプロトコル、コンテナなどに対する開発者の空気を見ると、人々は不安定なソフトウェアにようやく我慢できなくなってきているように感じる
第二に、ニッチ市場を狙っているようだ。CPUあたり1時間2ドル、予約時はCPUあたり年7000ドルで、趣味用途や自由・オープンソース向けの無料ティアはなく、試用や購入も問い合わせが必要だ。痛みはあるが有効なビジネスモデルではある。ただ、最大限のポジティブな影響を狙っていない点は惜しい
第三に、記事とドキュメントの品質が高く、ドキュメントに「バグが本番環境や顧客のところで見つかった場合は、私たちに説明を求めるべきです」のような文言が入っている点が本当に良い
開発者の好感を得る方法はまさにこれだ。以前私を失望させた後でも、今なお人に勧めているMullvadを思い出す
関連して https://news.ycombinator.com/item?id=39358526 で言及した。ちなみにAntithesisの共同創業者だ
ハードウェアもこれを支援する機能を追加し始めるかもしれず、30年後にはこれが単にコンピューティングの動作方式になっているかもしれない
ただし先駆者たちは本当に普及させる前に、まず最初に受けた矢のコストを回収しなければならない。これは一つの出来事ではなく、プロセスの始まりとして見るべきだ
ドキュメントを見ると、このプラットフォームが見つけるよう設計されているバグは、本番環境でまれにしか起きない、厄介な「再現不能」タイプだ
ほとんどのチームは、はるかに大きな問題や明白なバグを直さなければならない。実際、今日の本番ソフトウェアの大半は、単体テストすらかろうじてある程度だ
実際のユースケースでは、コストがどう積み上がるのか気になる
今年StrangeloopでAntithesisに会って社員たちと話したが、Amazonで働いていたときに追っていた自動障害注入の最新状況と比べても、この製品は現在使われている多くの形式検証システムより大きな飛躍だと思う
実際に、Apache Spark Streamingで彼らが見つけた問題のバグ追跡過程をたどることができた。ドキュメントを基準に見ると、一般的な作業で微妙かつ悪質な正確性エラーを見つけており、可視性の低い境界ケースで何年も頭痛の種になったであろう問題だった
最終的にはドキュメントが間違っていたという結論になったが、その過程を見た後では、分散システムを作る会社の中でAntithesisのようなツールがどれほど重要になるか、想像しにくいほどだ
近いうちに技術的な詳細に深く踏み込むブログ記事が出るといい。現在のアプローチにどう到達したのか聞いてみたい
すぐに過熱した期待のサイクルに飛び込むつもりはないが、これは聖杯のように聞こえる。既存アプリケーションをそのまま使い、コンテナ化されていると仮定すれば、その上でプロパティだけ検査すればよいのではないか?
いつも行き詰まっていたのは機械の基盤、つまり非決定的なCPUとOSだった
垂直方向のコンピューティングスタック全体を作り直すのは事実上不可能なので、彼らは高忠実度の決定的シミュレータを作って、その問題を回避したわけだ
ただし、シミュレータと既存OSの同等性をどう検査するのかは気になる。些細な作業ではなさそうだ。それでもこのアイデアにはかなり説得されている
その後、OS障害、ネットワーク問題、競合状態とタイミング条件、乱数生成器の問題など、あらゆる失敗を注入しながらテストを実行する
今日、こうしたものを信頼性高くテストする唯一の実用的な方法である可能性が高いが、それでもテストをすべて書き、アプリケーション状態を定義しなければならない
「ソフトウェアを受け取って、その中のバグを狩るプラットフォーム」だと言っていますが、では実際には何なのでしょうか?
統合テストを実行してくれるクラウドサービスのように見えます。この特別な環境にデプロイする方法を把握する必要があり、特殊なライブラリを使って統合テストもやはり書かなければならないようです。
ただ、そうやって統合向けのリファクタリングを全部やったとしても、自分の環境で自分の統合テストではすでに見つけられなかったはずの実際のバグを、どうやって見つけてくれるのか分かりません。
ただし Antithesis は手動テストや統合テストの作成を求めません。
ソフトウェアシステムをコンテナとしてパッケージ化する必要がありますが、これは比較的簡単で、その後にシステムの正常な動作をまねるワークロードを書く必要があります。たとえばECサイトなら、商品の閲覧、カートへの追加、決済などです。
これを基に Antithesis はワークロードを実行し、入力を変え、障害を注入しながらソフトウェアをテストし、テストプロパティ違反を探し始めます。
クラッシュ、メモリ不足など、60を超えるテストプロパティが標準で提供されています。システム固有の問題をさらに明らかにするには、カスタムプロパティも定義できますし、実際そうすべきです。
テストが実行されるとプロパティ違反が報告され、有用なデバッグ情報が多く含まれます。特に興味深いテスト実行については、巻き戻しや入力の変更、成果物の取得、ログの追加などが可能なので、さらに多くの分析ができます。
今のところ分かっているのはその程度です。何らかの形のファジングと静的解析、あるいはソフトウェアが実行できる動作の定義があるのだろうと推測しています。
正直、Vale 言語が解決しようとしているものと重なる部分が多いように見えます: https://vale.dev/
ただし、新しいソフトウェアが最初からそういう状態になるように新しい言語を作るのではなく、既存のソフトウェアをその状態に近づけることに焦点を当てているようです。
ハイパーバイザーを使って乱数シードを変え、HTTPリクエストを失敗させたり長時間かかるようにしたり、サーバー間の接続を切ったり、サーバー応答の順序を入れ替えたりと、普段は制御できないけれど現実では起こるあらゆることを作り出します。
その後、期待したワークロードの応答と比較して、どの条件がシステムを壊すのかを把握します。
だから年間契約で売っているのです。ワークロードを1年中実行し続け、あらゆる失敗の組み合わせを試させるために費用を払う、という仕組みです。
とても期待して少しドキュメントをざっと見ましたが、これがランダム化されたユニットテストとどう違うのかよく分かりません。
すでにユニットテスト一式があるなら、それが作業の99%ではないかと思います。私が誤解しているのでしょうか?
ドキュメントの Getting Started シリーズ、特に Workloads セクション https://antithesis.com/docs/getting_started/workload.html を読んで出した結論です。
How Antithesis Works ページを見ると、ユニットテストをただ束ねることとどう違うのかの答えになるかもしれません: https://antithesis.com/docs/introduction/how_antithesis_works.html
要約すると、ユニットテストはワークロードを構成する助けにはなり得ますが、必須ではありません。
私たちは、異なる入力や障害などを導入してソフトウェアシステムの実行経路を自律的に探索し、ユニットテストの作者が予想しきれなかったかもしれない挙動を発見します。
ネットワーク呼び出しを行って結果をディスクに書き込む関数のテストを書いた場合、ネットワーク呼び出しが失敗したり無期限に停止したりするケース、ディスク容量が不足するケース、ファイルを閉じる直前に電源が落ちるケースなどをコードが処理できなければ、テストは失敗します。
つまりその通りではありますが、ユニットテストのように簡単にテストできる領域を、はるかに興味深い複雑さのレベルまで広げるものです。