分散型 SQLite の 12 年
Raft ログを SQLite の手前に置いた最初のシステムは 12 年目になった。著者が、週末のプロジェクトがどう一つのカテゴリーになり、資金が誰に流れたかを振り返る。
日本語
コピー

2014 年 9 月、短い投稿で rqlite を公開した。アイデアは単純だ。SQLite を用意し、その手前に Raft ログ を置いて、すべての書き込みをクラスタに複製する。ノードが 1 台落ちてもデータは失われない、という仕組みだ。ソースコードはその週末には GitHub に上がっていた。私の知る限り、Raft と SQLite を組み合わせたオープンソースのシステムとしてはこれが最初だった。当時ほかに作っている人はおらず、その後何年も誰も試みなかった。

今日、「分散型 SQLite」は十数プロジェクトを数えるカテゴリになった。そのうちいくつかはベンチャーキャピタルから出資を受け、2 つは買収されている。このカテゴリについて書く人はたいてい 2021 年から語り始めるので、ここでは 2014 年から語る版を書いておきたい。私はその全過程をその場で見てきた。
2014:rqlite
必要とされていたものは具体的だった。私が jut.io で関わっていたクラスタシステムには少量のリレーショナルストレージが必要で、どのノードがそれを持つべきかについて良い答えがなかった。SQLite を合意プロトコルで複製すればこの問題は解決する。仕事では結局別の道を選んだが、この問題は普遍的なもので解法は小さいと感じ、rqlite を作り続けた。最初のバージョンは goraft を使っていた。当時 InfluxDB と etcd も同じライブラリを使っている(私自身もほぼ同じ時期に、コア開発者として InfluxDB に加わった)。2016 年初頭、Hashicorp の Raft 実装に差し替えた。rqlite は今もこれを使っている。
最も重要な設計判断はこの頃に下され、その後一度も変わっていない。rqlite は CP システムだ。書き込みはリーダーを経由し、多数派のノードに確認されなければならず、分断された少数派は書き込みを拒否する。HTTP API、読み取りの一貫性レベル、スナップショット、その他すべてはここから導かれる。
2014 年より前、および同時期の試み
正直に言っておくべきことが 2 つある。Expensify は何年も前から自社インフラで Paxos 系のプロトコルを使って SQLite を複製しており、2016 年に Bedrock という名前で公開した。もう 1 つは ActorDB で、各「actor」が自分の SQLite データベースを持ち、それを Raft で複製する Erlang 製のシステムだ。rqlite とほぼ同時期に登場した。どちらも生まれた場所の外で広く使われることはなかったが、このアイデアが当時すでに流れていたことは示している。rqlite がやったのは、それを独立した汎用的なオープンソースとして、誰でも動かせる形にすることだった。
2017:dqlite
Canonical は dqlite を作った。LXD、後に MicroK8s に、etcd を動かすことなく組み込みの複製ストレージを持たせるためだ。Mark Shuttleworth から実際に一度電話をもらい、rqlite を彼らの求める形に作り変えられないかと話したが、どうにも噛み合わなかった。dqlite は当初 Go で書かれ、後に C で書き直され、独自の Raft ライブラリを伴う。意図の上では rqlite に最も近いプロジェクトで、主な違いはパッケージングにある。dqlite は組み込むライブラリ、rqlite は動かすサーバーだ。Canonical が必要としていたのは製品の内部にあるデータベースで、私が必要としていたのはあらゆる製品の隣に置くデータベースだった。
2021:Litestream、そして発想の転換
Ben Johnson(InfluxDB 時代の同僚だ)は2021 年初頭に Litestream を公開した。Litestream は合意アルゴリズムを一切使わない。SQLite の WAL を監視し、その内容を S3 にストリーミングすることで、単一ノードの SQLite データベースにほぼゼロコストの災害復旧を提供する。Ben の主張はこうだ。ほとんどのアプリはクラスタを必要とせず、1 台のマシンで動いており、必要なのはマシンが落ちた後に復旧できることだ。かなりの数のアプリについて、彼は正しい。その後の数年間に起きた「SQLite で十分」という潮流——あのブログ記事やカンファレンス講演の数々——は、主にこの主張から生まれている。
Litestream と rqlite は別の問題に答えている。Litestream:マシンが落ちたらどうする?rqlite:マシンが落ちて、復旧を待つ時間がないときはどうする?前者のほうがよくある問題で、後者こそが人が rqlite にたどり着くときに本当に気にしている問題だ。
Fly.io は 2022 年に Ben を雇い、LiteFS を発表した。ファイルシステムのレベルで SQLite を複製し、Consul のリースを利用する。複製型 SQLite を、自分でデプロイするものではなくプラットフォームの機能にしようとした最初の試みだ。最後のひねりとして、Litestream は rqlite v8.0 が SQLite の WAL を扱う方法に影響を与えた。
2022:分岐と拡張
2022 年の 2 つのプロジェクトは、rqlite も Litestream も通らなかった道を進んだ。cr-sqlite は拡張として SQLite に CRDT を加え、複数の書き込み側がそれぞれ分岐して後からマージできるようにする。リーダーは不要だ。設計空間の AP の一角で、local-first アプリに向いている——そうした場面では rqlite の CP の保証はむしろ足かせになる。
libSQLは SQLite そのものを fork した。ChiselStrike(後にTursoになる)はエンジン内部に手を入れたいと考えていたが、SQLite プロジェクトは外部からのコントリビューションを受け付けず、変更もしない。彼らは fork の上に、エッジレプリカ付きのマネージドサービスを構築した。Turso はこれでベンチャーキャピタルから資金を調達し、2026 年にSupabase に買収された。
ほぼ同じ時期にSQLite Cloudが登場し、Raft レプリケーションによるマネージド SQLite を提供した。rqlite の当初の設計に最も近いプロジェクトだが、クラウドサービスの層で包まれている。
2022 年以降:プラットフォーム
Cloudflareは 2022 年にD1をリリースし、SQLite をサーバーレスデータベースとして自社のエッジネットワークに組み込んだ。その後Durable Objectsでも SQLite ストレージを使えるようにした。レプリケーション版 SQLite をマネージド製品にするということだが、その会社は仕組みを説明する必要がない。Turso のサービスも同じ道だ。このカテゴリは「自分で動かすもの」から「金を払って買うもの」に変わった。
12 年が示すもの
3 つある。
コアは正しかった。SQLite をストレージエンジンとして使い、外部でレプリケーションを重ねるという手法は、少なくとも 8 つのチームがそれぞれ独自に再発明してきた。要求もプロトコルもばらばらだ。それでも、やめる理由を見つけた者はいまだにいない。
設計空間には 3 つの角があり、しかも順番に現れた。まずコンセンサス(rqlite、dqlite)。単一リーダー、強い一貫性、1 つのクラスタ。次にストリーミングレプリケーション(Litestream)。単一ライター、復旧が安い、クラスタなし。最後にマージ(cr-sqlite、および local-first 運動の一部)。複数ライター、結果整合性。それぞれが異なる問題に対応しており、議論のほとんどは、互いのユーザーが相手はどの問題を問うているのかを争っているにすぎない。
金はプラットフォームに流れた。rqlite、dqlite、Litestream はオープンソースで無料だ。資金を調達したプロジェクトや買収されたプロジェクトは、レプリケーション版 SQLite を請求書付きのサービスにしたものばかり。インフラ系のアイデアはたいていそうなるもので、私自身は気にしていない。ただ、一言記しておく価値はある。このアイデアはオープンソースになって 8 年経ってから、誰かが売り始めたのだ。
rqliteは今もある。やっていることは 2014 年と同じで、ただかなり良くなった。フォールトトレラントなリレーショナルデータベースを、自分で動かし、すべてを理解でき、誰にも金を払わずに欲しいとき、今でもこれが選択肢になる。12 年経った今、物語の中でのこの位置に私は満足している。