HN 日本語サマリー

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

純粋なZigでゼロから構築する検索・推論データベース

A search-and-inference database from scratch in pure Zig (antfly.io)

28 pointsby kingcauchy6 コメント

要約

Antflyは、当初Goで開発されたドキュメントストレージ、フルテキスト、ベクトル、グラフインデックスエンジンでしたが、依存関係をゼロにし、パフォーマンスと移植性を向上させるために、純粋なZigで再構築されました。著者は、Zigの可読性、C言語との相互運用性、速度、テスト容易性といった特性が、特にAIやセマンティック検索といった新しいユースケースに対応するために最適であると説明しています。

全文翻訳

リサーチに戻る 1年以上前、同僚のRowanがAntflyの野心的な目標を要約しました:完璧な検索!この目標は愚かで、野心的で、達成不可能であり、私たちにぴったりです。完璧な検索について壮大な技術者たちが語るのを聞くのは、まるでSilicon Valleyのエピソードから飛び出してきたかのようですが、私は不可能な目標が好きですし、私の内なるテックヒップスターはその皮肉を高く評価しています。完璧な検索を目指すと、見逃しますが、興味深い場所で見逃します。 そこで、新しいスタートアップでは決してやるべきではないことを、なぜやったのかを説明します。私たちは製品を書き直しました。Antfly v0.1は、Goでドキュメントストレージ、フルテキスト、ベクトル、グラフインデックスエンジンをローンチしました。Antfly v0.2は、同じエンジンをZigでローンチします。依存関係はゼロです(まあ、ショーを動かすものはありませんが、それについては後述します)。 第一原理 第一原理について以前ほど話を聞かなくなりましたが、それは私と私の意思決定にとって依然として重要です。Antflyの最初のバージョンを構築する際、データベース市場のギャップを埋めるという考えがありました。Elasticsearchのようなスキーマ的でフレンドリーなクエリエンジンで、スケールはIcebergよりもPostgresに近く、Mongoのように使いやすく、GoogleのSpannerやBigtableのように運用しやすいものです。(そして、完璧な検索は高望みすぎると考えていました…HA!) 最初のバージョンを構築する際、私は(咳、咳、Codex、Claude、Aider、Piのような)同じようなソフトウェアツールを利用できませんでした。そして、私が取り組みたい困難な問題は、CockroachDBが分散システムと並行処理のためにGoを選んだのと同じようなものでした。Rustはライフタイムを通じてメモリ安全性を保証できますが、メモリ安全性は難しい部分ではなく、RustよりもGoの方が私には無限に読みやすいです。さらに、Goは最も戦術的にテストされたRaft実装(etcdのもの)を持っており、私は金曜日と週末に一人で作業し、Bleveの上にElasticsearch DSLを構築しようとしていました。 埋め込みではなく、モデルを気にする 少し早送りすると、埋め込み(embeddings)が流行り始めました。word2vecは、ベクトルが実際に意味を運ぶことができることを示し、最初の実用的な埋め込みモデルは、平均的な開発者がGoogle Liteのようなセマンティック検索を自分のアプリに構築できることを意味しました。私の職場では、ベクトルストレージの規模は非常に小さかったため、トップKは網羅的な検索になり得ましたが、古い同僚がridiculousなPineconeのシードラウンドを鼻で笑った後、Pinecone、Vertex AI Vector Search、Elastic、Mongo、CockroachDB、pgvectorの背後にあるすべての楽しいアルゴリズムについて読みました。 Pineconeは過大評価されていたかもしれませんが、その最も興味深いアイデアの1つが見過ごされていたと思います。それは、ユーザーが埋め込みを気にする必要がなく、モデルを気にするだけでよいということです。これはPostgresについて考えさせられました。ユーザーは、Bツリーやその他のインデックスアルゴリズムについて知る必要がない場合が多いです。あるいは、BleveやLuceneでは、S2インデックス、有限状態トランスデューサーなどを知る必要がない場合です。一方、ベクトルデータベースやインデックスは、HNSW、SPFresh、RaBitQなどを知って気にする必要がありました。 私は、Antflyの初期バージョンで、これらのアルゴリズムを論文やブログ記事から実装することに挑戦しました。それは大成功でした。私のラップトップ、Antfly、Ollamaを使って、セマンティックに検索可能なWikipediaを作成することができました!埋め込み生成は非常に遅かったため、データベースが少し遅いことは初期実装では大きな問題ではありませんでした。 Zigの登場 これと並行して、数年前にTigerBeetleに出会った後、ZigでVSR、Protobuf、LSMを実装しようとしました。Zigの読みやすさ、並行処理プリミティブ、言語を実装している人々、そしてCとの相互運用性が非常に気に入りました。しかし、Zigは私の週末データベースプロジェクトにはまだ青すぎ、多くの重労働が欠けていました:Raft、フルテキストインデックス、ポータブルで戦術的にテストされたLSMなどです。 しかし、TigerBeetleの人々の精神の多くを私自身に取り込み、VOPRテストをTLA+トレース検証と組み込む作業に没頭しました(コーディングエージェントによる形式検証に関するRowanの投稿がそれをカバーしています)。これは新しいGoのモック時間と、etcdのRaft TLA+仕様とトレース検証からの先行事例に基づいて構築されました。 その後、Zigが言語にいくつかの真剣なオーバーホールを行うと、std.Ioはテクノバース全体で大きな波紋を広げ始めました。私はこの件に関するGitHubのデザインスレッドをしばらく読んでおり、エンジニアリングの観点からはすべて非常にクールだと思っていました。 Antflyはちょうどv0.1をリリースしたばかりで、次に何が来るか考え始める余裕がありました。そこで、Zigと新しいstd.Ioの作業を試し始め、Zigが公開し始めた非同期I/Oとstd.Io.Eventedメカニズムを使用してLSMを実装しようとしました。同時に、コードのソフトウェア分解をどこまで進められるかを見たかったのですが、Raft、TLA+仕様、明確なトレース、そして言語に依存しないテストが、登るべき完璧な丘のように感じられました。 そこで、私はトラフィックを管理し、Raft、フルテキストインデックス、LSM、そして…HTTP/2(そしてHTTP/3とQUIC上のRaftトランスポート)を構築する作業に取り掛かりました。 また、GoベースのエンドツーエンドテストのほとんどをPythonで書き直しました。これは、コーディングエージェントがGoコードにアクセスして「不正行為」できないようにするため、そしてPython SDKが堅牢であることを確認するためでした。これは、Zigにトランスパイルするための完璧な言語に依存しないフレームワークであることが判明しました。 なぜ書き直したのか Jamesとの会話とDrewとの別の会話が、私たちが何を最適化しているのかについて話したときに、このアイデアを固めました:非対称な結果と信頼性。ほとんどのスタートアップは失敗するため、すべての高性能な依存関係を自社で書くという高価な賭けは、行う価値のある賭けです。 ユーザーは、自分のデータベースが機能することを「知って」信頼しなければならず、ユーザーはそのデータベースが「飛ぶ」ことを望みます、競走すること(驚くほど速くある必要がある)ではなく。 人々はすでにAntflyの上にさまざまな興味深いプロジェクトを構築していました。しかし、他のどのプログラミング言語からでも、あるいはブラウザで実行するためには、RustやZigのようなものが必要で、C互換、WASM互換、WebGPU互換のインターフェースを提供する必要がありました。 私はAntflyをデータベースのグランドユニファイドセオリーにしたいと思っていました:古いユースケースや従来のアプリケーションのためのマチェーテ、そしてまだ誰も見たことのないAIやセマンティックユースケースのための完璧なスイスアーミーナイフ(完璧な検索はもはや高望みすぎないように思えました)。開発者がサンドボックス化された環境(ラップトップ、単体テスト、Lambda)に埋め込むことができ、平均的なアプリケーションワークロード(PostgreSQL、Cockroach、Mongo)で実行でき、あるいは分析スケール(データウェアハウス、サーバーレス)で実行できるものです。 そして、設定したパフォーマンス目標を維持または超えながら、すべての環境でAntflyをさらに信頼性の高いものにしたいと考えていました。 すべてをゼロから書き直すことで、リソース管理、優先度スケジューリング、テストイデオロギーを最初から組み込み、目的意識を持った形状で依存関係をすべて構築する機会が得られます。 では、なぜRustではなくZigなのか?4つの理由、すべて第一原理に戻ります:移植性、Cインターオペラビリティ、速度、そしてテスト容易性。 移植性とCインターオペラビリティ:Zigは、すべてのターゲットにlibcを持つCコンパイラです。CUDA、ONNX、WasmtimeをリンクしたAntflyのクロスコンパイルは1つのフラグで済み、それらを呼び出すのは@cImportです。Rustは純粋なRustはうまくクロスコンパイルしますが、最初のC依存関係で壊れます。だからcargo-zigbuildはZigなのです。 速度:同じLLVMなので、コード生成は同等です。違いは、速いバージョンがデフォルトであることです。すべての割り当てはアロケータを取り、何もバックグラウンドで割り当てたり分岐したりせず、SIMDとコンパイル時(comptime)の特殊化が組み込まれています。Rustではホットパスはunsafeに入ります。Goと比較すると、全く比較になりません:GCなし、スケジューラなし、CGOなし。 テスト容易性:std.Ioは世界をパラメータにします。パッケージにシミュレートされたIoを渡すと、VOPR(ディスク、ネットワーク、クロック障害など)が得られます。Rustでは、tokioをmadsimやturmoilに置き換えて、依存関係ツリーが協力してくれることを願うだけです。 人々は通常、形式検証とメモリ安全性が、バグのクラス全体からあなたを守ってくれると考えていますが、