Rustで使える埋め込み型のkey-value storeとして、TurboKVというライブラリがGitHubで公開されています。単に「速いDBです」で終わる話ではなく、非同期API、batchの原子性、range scan、耐久性の切り替え、圧縮、バックグラウンドcompactionまで含めて、かなり実戦的な機能をひと通り揃えているのが目を引きます。Rustで小さく始めたいが、あとから保存方式や一貫性で困りたくない、という人には気になる存在です。今回はそのREADMEを軸に、何を目指したDBなのかと、そこから見える設計の匂いを追います。
TurboKVは「A fast, simple, and embedded key-value store for Rust」と名乗る、非同期の埋め込み型データベースです。READMEでは、atomic batches、ordered range scans、configurable durability、compression、background compaction を備えると説明されています。つまり、単純な get / put だけのストレージではなく、アプリの中に組み込みながら、実用DBらしい挙動を狙っているわけです。
導入は cargo add turbokv と cargo add tokio --features full で始められます。コード例では Db::open_with_options("./my-database", DbOptions::durable()) で開き、insert、get、WriteBatch を使った複数操作の一括書き込み、scan_prefix による prefix scan、最後に close() まで行っています。READMEはこれを「Runnable examples」として整理しており、basic、batch_writes、range_queries、concurrent、persistence、configuration というサンプルも用意されているようです。
面白いのは、耐久性のモードが三段階に分かれている点です。DbOptions::fast() は WAL を使わず、メモリ上での可視性だけを優先します。DbOptions::durable() は WAL に追記するが、毎回 sync するわけではなく、プロセスクラッシュからの回復と、電源断に備えた定期的な checkpoint を想定した既定値です。DbOptions::paranoid() は、返答前に sync_all まで完了させる最も強いモードです。
READMEはここをかなり丁寧に説明しています。durable モードでも WAL が「ずっと非同期のまま」ではなく、明示的またはバックグラウンドの memtable flush、クリーンな close、あるいは WAL segment のローテーションで同期される、と書いています。ただし、64 MiB を超えたら必ずその分だけ失う、というような厳密な上限ではないとも注記しています。flush は非同期で、メモリ計測は近似値、大きな mutation が境界をまたぐこともあるからです。だから、成功応答のたびにストレージへの sync barrier を越えてほしいなら paranoid を使え、という整理です。
データ操作の仕様も細かいです。キーと値は AsRef<[u8]> で受け取り、文字列は呼び出し側でエンコードします。insert は置き換え、remove は tombstone を書く、take は値を返しつつ削除する、contains_key は get と同じ状態確認をするが今のところ値の allocation を伴う、といった具合です。write_batch は batch 全体を原子的に公開し、重複キーは最後の操作が勝ちます。insert_many は bulk API だが atomic な可視性切り替えではない、と区別しているのも実務的です。
さらに、範囲読み取りは raw bytes の辞書順で並び、range(start, end) と scan_prefix(prefix) は point-in-time の一貫したビューを返すとされています。一方で、これらの scan は active memtable を凍結することがあり、細かい scan を頻繁に行うと flush の負担が増える、といった注意も書かれています。API だけでなく、使い方次第で内部動作にどんな影響があるかまで見せているのが特徴です。圧縮は Lz4、Snappy、Zstd、None から選べ、既存テーブルはその時点の形式を保ちます。READMEの最後のほうには、ファイルをまたいだ排他所有、close() でのクリーン shutdown、drop は shutdown contract ではない、といった運用上の注意も並んでいます。
TurboKVの設計でまず目を引くのは、性能の話と安全性の話を別々にしないところです。多くの小さな埋め込みDBは、最初は軽快でも、耐久性の条件が曖昧だったり、batch が本当に原子的なのか読み取り時の見え方が分かりにくかったりします。TurboKVはそこを隠さず、fast / durable / paranoid を前面に出しています。これは親切というより、むしろ責任の切り分けがうまいと思います。アプリ側が「今は速さ優先」「ここは失って困る」「この1件だけは絶対落とせない」と段階的に選べるからです。
特に、READMEが durable モードの限界をかなり具体的に書いている点は好感が持てます。ふつうなら「durableです」で済ませたくなるところを、WAL の sync、memtable flush、segment rotation のどれで同期が起こるのかまで説明している。これはユーザーにとっては地味ですが重要で、障害時に「どこまで守られるのか」を誤解しにくい。逆に言うと、ここを曖昧にしないライブラリは、それだけ本気で実運用を意識しているとも読めます。
TurboKVは API の見た目がかなり整理されています。insert、get、remove、take、write_batch、scan_prefix と、アプリ側が欲しくなる操作をそのまま並べている。しかも WriteBatch は自前で key/value を保持し、ops() で中身も覗ける。こういう設計は、実装の裏側を想像させすぎず、それでいて挙動は読みやすい。Rust の利用者は型や所有権に厳しいぶん、「何がコピーされ、何が owned なのか」が明確なライブラリを好むはずで、TurboKV はそこにかなり寄せています。
一方で、contains_key が今のところ allocation を伴う、と素直に書いているのも印象的です。これは理想形ではないけれど、無理に隠さず明記している。こうした注記があると、後でボトルネックを追いやすい。Rust では速さを期待して使い始めることが多いだけに、API の便利さより「どこでコストが出るか」を先に示すのは賢いと思います。小さな DB ライブラリほど、こういう透明性が信頼につながります。
README の中で少し異質なのは、WAL、memtable、checkpoint、flush、compaction といった内部機構の説明がかなり前に出てくることです。普通の紹介文なら「永続化できます」で終わるところですが、TurboKV は「同期されるのはどの時点か」「どの条件なら失われうるか」をかなり明確に書いています。これは実装者の癖というより、使う側に事故を起こさせたくない姿勢に見えます。
実際、埋め込みDBは「入れたのに消えた」「落ちた後に整合しない」といった事故が起きると、アプリ全体の信用が落ちます。だからこそ、WAL を有効にしていても sync_writes が必須ではない、逆に paranoia モードなら強い保証がある、という段階設計は意味があります。全ユーザーに最強保証を強制すると遅くなるし、全員に最速構成だけを渡すと事故る。TurboKV はその中間を最初から製品の顔にしている。これは単なる機能の多さではなく、壊れ方まで設計に含めたDBだと感じます。
TurboKVは埋め込みDBですが、巨大な汎用DBの代替を正面から狙っている感じではありません。READMEの語り口は、アプリの内部ストレージとして、必要な分だけしっかり動くことを重視しています。scan_prefix や range_iter があり、background compaction があり、圧縮方式も選べる。けれど同時に、Db::open がディレクトリを排他的に所有することや、close() がクリーン shutdown の前提であることまで書いてある。これはライブラリを雑に埋め込んで壊す使い方を避けたい、という意思表示にも見えます。
こういうDBは、イベントログ、キャッシュの永続化、ローカルなメタデータ管理、ワークキューの状態保存みたいな用途で効きやすいはずです。特に Rust だと、async と所有権の境界が複雑になりがちなので、Tokio 前提で API が揃っているのはありがたい。もちろん、READMEだけでは実測性能や競合DBとの比較は分かりません。そこはまだ判断保留です。ただ、少なくとも「何を守り、何を捨てるか」がかなり見えている。そこがこのプロジェクトの一番の価値ではないかと思います。
参考: GitHub - kingroryg/turbokv: A fast, simple, and embedded key-value store for Rust.