SQLiteはどのようにテストされているのか
(sqlite.org)- SQLiteは徹底した自動化テスト体制によって高い信頼性と堅牢性を維持しており、コードの590倍ものテストコードが存在する
- 4つの**独立したテストハーネス(TCL、TH3、SQL Logic Test、dbsqlfuzz)**がコアライブラリを検証し、数億件のテストを実行する
- **異常系テスト(OOM、I/Oエラー、クラッシュシミュレーション)とファズテスト(fuzz testing)**により、異常入力やシステム障害でも安定して動作することを確認する
- 100%の分岐およびMC/DCカバレッジ、リソースリーク検出、Valgrind・静的解析・チェックリストなどの多層的な検証手順を維持している
- このような体系的テストにより、SQLiteは商用DB並みの信頼性と品質を備えたオープンソースデータベースと評価されている
1. 概要
- SQLiteの信頼性と堅牢性は、緻密なテスト工程に由来する
- バージョン3.42.0時点で、SQLiteは約155.8 KSLOCのCコードと92053.1 KSLOCのテストコードで構成される
- テスト体制には4つの独立ハーネス、100%の分岐カバレッジ、数百万件のテストケースが含まれる
- OOM、I/Oエラー、クラッシュ、ファズ、境界値、回帰、不正なDBファイル、最適化無効化テストなど多数の項目を含む
2. テストハーネス
- TCL Tests
- SQLite開発中に主に使われる公開テストセット
- 27.2 KSLOCのCコードと1390個のスクリプトファイル(23.2MB)で構成される
- 約5万件超のテストケースがあり、パラメータ化により全実行では数百万件に達する
- TH3
- 商用のCベースのテストセットで、100%の分岐およびMC/DCカバレッジを達成
- 組み込み環境でも動作し、1055.4 KSLOC・約5万件超のケースを含む
- 全カバレッジテストでは約240万件、リリース前には2.48億件のsoakテストを実施する
- SQL Logic Test (SLT)
- SQLiteとPostgreSQL、MySQL、SQL Server、Oracle 10gの結果を比較する
- 720万クエリ、1.12GBのデータで構成される
- dbsqlfuzz
- SQLとデータベースファイルを同時に変異させるlibFuzzerベースのファザー
- 1日あたり約10億回の変異テストを実行し、悪意ある入力に対する堅牢性を検証する
- 追加ツール
- speedtest1.c、mptester.c、threadtest3.c、fuzzershell.c、jfuzz など
- すべてのテストは複数プラットフォーム・コンパイル設定で通過して初めてリリース可能となる
3. 異常系テスト
- OOMテスト
- malloc() の失敗をシミュレートし、メモリ不足時に正常復旧できるかを検証する
- 失敗タイミングのカウンタを増やしながら繰り返し実行する
- I/Oエラーテスト
- 仮想ファイルシステム(VFS)を使ってディスクエラーをシミュレートする
- エラー後に
PRAGMA integrity_checkでデータ破損の有無を確認する
- クラッシュテスト
- 電源断やOSクラッシュの状況をシミュレートする
- TCLハーネスは子プロセスベース、TH3はメモリベースVFSを使用する
- トランザクションが完全にロールバックされるか、完全に完了するかを検証する
- 複合障害テスト
- クラッシュ後にOOMまたはI/Oエラーが連続発生する状況まで検証する
4. ファズテスト
- SQL Fuzz
- 文法的には正しいが異常なSQLを生成し、SQLiteの反応を検証する
- American Fuzzy Lop (AFL)
- 2014年に導入されたプロファイルベースのファザーで、新しい制御経路を探索する
- SQLiteのassert失敗・クラッシュ・誤った結果を多数発見した
- Google OSS Fuzz
- 2016年からGoogleのインフラ上で自動ファジングを実施
- 新規コミットで断続的に発生する問題を検出する
- dbsqlfuzz / jfuzz
- 2018年以降に内部ファザーとして導入され、SQLとDBファイルを同時に変異させる
- 1日5億件以上のテストを実施し、外部ファザーからのバグレポートはほぼ消滅した
- 2024年からjfuzzがJSONB入力の検証を追加した
- サードパーティ製ファザーとfuzzcheck
- 外部研究者(例: Manuel Rigger)が誤った結果計算の事例を多数発見した
- fuzzcheckユーティリティは、過去のファズケースのうち「興味深い」数千件を再検証する
- MC/DCとファズテストの緊張関係
- MC/DCは防御コードの最小化を志向し、ファズは防御コードを必要とする
- SQLiteは両アプローチを並行して採用し、正常入力・悪意ある入力の両方に堅牢なコードを維持している
5. 回帰テスト
- 報告されたバグは修正後、必ず新しいテストケースとして追加される
- 過去のバグの再発防止が目的
6. 自動リソースリーク検出
- TCL・TH3ハーネスがメモリ・ファイル・スレッド・ミューテックスのリークを自動監視する
- OOM・I/Oエラー後でもメモリリークがあってはならない
7. テストカバレッジ
- SQLiteコアはTH3基準で100%の分岐カバレッジを達成している
- FTS3、RTreeなどの拡張は除く
- Statement vs Branch Coverage
- 分岐カバレッジは文カバレッジより厳格で、すべての条件分岐を両方向で検証する
- 防御コードカバレッジ
- ALWAYS()、NEVER() マクロで防御条件を明示する
- 3種類の定義形でテストを繰り返し、一貫性を検証する
- 境界値およびブールベクトルテスト
- testcase() マクロで条件の真・偽の両結果を検証する
- 1184個のtestcase()を使用
- MC/DC達成
- testcase() マクロにより、すべての条件の独立した影響を検証する
- gcovベース測定
-fprofile-arcs -ftest-coverageオプションでカバレッジを測定する- 結果比較を通じてコンパイラバグや未定義動作を検出する
- Mutation Testing
- 分岐命令を変更し、テストがそれを検知できるか確認する
- 最適化分岐(
/*OPTIMIZATION-IF-TRUE*/)は例外扱い
- 完全カバレッジの経験
- すべての分岐をテストすることで、コード変更時の副作用を最小化できる
- 保守コストは高いが、広範に配布されるインフラライブラリとして正当化される
8. 動的解析
- Assert()
- 6754個のassert文で事前条件・事後条件・ループ不変条件を検証する
SQLITE_DEBUGビルドでのみ有効
- Valgrind
- メモリエラー・スタックオーバーフロー・未初期化メモリアクセスを検出する
- リリース前にveryquickおよびTH3テストをValgrindで実行する
- Memsys2
SQLITE_MEMDEBUGビルド時にメモリエラー監視用ラッパーを挿入する- Valgrindより高速に反復検証できる
- Mutex Asserts
sqlite3_mutex_held()などでマルチスレッド同期を検証する
- Journal Tests
- ロールバックジャーナルがDBより先に記録されるか確認し、トランザクションの原子性を保証する
- Undefined Behavior Checks
-ftrapv,-fsanitize=undefined,/RTC1などで未定義動作を検出する- 32/64ビット、エンディアン、さまざまなCPUアーキテクチャで繰り返し実行する
9. 最適化無効化テスト
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)で最適化を無効にする- 最適化の有無にかかわらず同じ結果を出力しなければならない
- 一部の性能測定用テストは例外
10. チェックリスト
- リリース前に約200項目の手動チェックリストを検証する
- 数秒で終わるものもあれば、数時間かかるものもある
- 問題が見つかれば即座に項目を追加し、継続的に改善する
11. 静的解析
- GCC・Clang・MSVCで警告なしにコンパイルされる
- Clang Static Analyzerでも有効な警告はない
- 静的解析は実際のバグ検出効果が限定的
12. 要約
- SQLiteはオープンソースでありながら商用品質と低い欠陥率を維持している
- 徹底したテストとコード設計が中核要因
- すべてのリリースは上記手順を経て、ミッションクリティカル環境でも信頼できるDBエンジンとして提供される
4件のコメント
あわせて読むとよい記事: SQLiteの知られざる物語
SQLite開発者のRichard Hippへのインタビューを要約した文章です。
SQLiteの開発者たちは、ロックウェル・コリンズで働いていた時期にDo-178を知り、この手順に従い始めたそうです。その一つが100%のMC/DC達成です。
Do-178は本当に有用な指針書なので、開発者なら誰でも読んでみることをおすすめします。
これですか? https://alm.parasoft.com/hubfs/…
リンクされたものは、Do-178 の教育資料のようです。
元の文書は、このリンクをご覧ください。
https://studylib.net/doc/27132454/rtca-do-178b
Hacker Newsの意見
10年ほど前、SQLiteのメンテナがOSCONでテストの実践について発表していた
特に印象的だったのは、チェックリスト(checklist) の力だった。飛行前にパイロットが使う、まさにあの道具だ
また彼は国境なき医師団(Doctors Without Borders) の事例にも触れており、医療スタッフ同士が互いの名前も知らず、言語も異なっていたため、手術の成果が低かったという
解決策は単純で、手術前のチェックリストを作り、それぞれが自分の名前と役割を言うようにしたことだった。この小さな儀式が、技術ではなくコミュニケーションの改善によって生存率を高めたという
関連資料: SQLite checklistの例
良いチェックリストと悪いチェックリストの違いについて、もっと議論が必要だ。数学の美しい公式のように単純に見えるが、見つけるのは難しい気がする
特に米陸軍のFM22-100文書を何度も読んだが、驚くほど現代的で刺激的だ
FM22-100文書を見る
書籍リンク
自分はテストやCIに加えて、Markdownで書かれたデプロイチェックリストにも従っている。結果を保存すらしないが、段階ごとに実行している
こんなに単純なことを、なぜ他の人はやらないのかわからない
MSFの事例を扱った公式ページがあるならぜひ見たい。ググっても見つからなかった
SQLiteのテストに関する過去の議論がまとめられている
2009〜2024年のHNスレッド一覧
1年おきに再投稿が繰り返されているようだ
SQLiteのようなソフトウェアを完璧に磨き上げる過程が羨ましく、驚嘆させられる
職人芸を感じる作品だ
時間が経てば品質基準は上がり、同じ努力でより大きな報酬を得られるようになる
自分が手を入れた部分を少しでもきれいに残す人を嫌う者はいない
SQLiteは本当に素晴らしいソフトウェアだ。公式サイトもマーケティングより情報重視で好感が持てる
ただ最近HNに公式サイトのページが1つずつ上がってくるのは興味深い
こうしたリンクをまとめておくと面白そうだ
SQLiteが公開ソフトウェアでありながら非公開テストを使っている点は興味深い
オープンソースプロジェクトがクローズドなテストを持てるのだと、今になって気づいた
こうしたモデルはオープンコア(open-core) に似た新しいビジネスモデルになるかもしれない
例: railgunlabs/unicornライセンス
SQLiteの100%ブランチカバレッジは、プロジェクト自体と同じくらい印象的だ
それを継続的に維持しているのが特にすごい
テストが非公開という点は興味深い。LLMベースのコーディングが進化する今、テストが実装より重要になる時代が来ている
最近、simonwがjustHTMLエンジンをPythonからJSへほぼ自動変換した事例を見て、SQLiteのテスト戦略を思い出した
最近SQLiteとDuckDBの互換性を考えながらLLMと議論したが、同時実行処理の面ではSQLiteの方が優れているという結論になった
SQLiteのテスト文書で、性能回帰テスト(performance regression) への言及が少ないのは意外だった
正しさも重要だが、特定のクエリ経路での性能低下は致命的になりうる
こうした目標を中核的な使命にしているプロジェクトがあるのか気になる
SQLiteの安定性を見ると、異常(anomaly)テストがどう行われたのかもっと知りたくなった
だが記事ではほとんど触れられていなかった。それでもSQLiteは、あらゆるデバイスで使われる最も堅牢なソフトウェアの一つだ