SQLiteにおけるトランザクションと仮想テーブル
(misfra.me)- SQLiteの仮想テーブルも書き込みとトランザクションをサポート可能で、
xUpdate、xSync、xCommit、xRollbackなどのフックを実装して利用する - SQLiteは基本的に ロールバックジャーナル方式で原子性 を保証し、複数のDBファイルを扱う場合は スーパージャーナル で全体のコミットを調整する
- 仮想テーブルもSQLiteのトランザクションプロトコルに含まれ、
xSyncが失敗するとトランザクション全体をロールバックする - コミットは 2段階に分かれており、
xSyncは失敗の可能性がある処理、xCommitは単純なクリーンアップ処理のみを行うべき xCommitとxRollbackは常に呼び出されうるため、失敗せず実行できるクリーンアップ用関数として実装すべき
SQLiteの仮想テーブルとトランザクション処理
前回の記事では、Go言語を使ってSQLiteの仮想テーブルを登録し、クエリする基本的な方法を紹介した。今回は、書き込み可能でトランザクションをサポートする仮想テーブルの実装方法を扱う。
仮想テーブルの書き込みとトランザクション対応
-
SQLiteの仮想テーブルインターフェースは 読み取り専用ではない
-
xUpdateフックを実装すれば、外部データソースにも書き込み可能 -
真のトランザクション整合性のためには、次のようなトランザクションフックが必要:
xBegin: トランザクション開始の通知xSync: ディスクへ安全にコミットするための準備(ここで失敗すると全体をロールバック)xCommit: 最終コミットとクリーンアップxRollback: トランザクションが中断された場合のロールバック処理
-
通常のテーブルや他の仮想テーブルと一緒に変更される場合でも、SQLiteは すべてのフックを連携させて原子性を保証する
SQLiteトランザクションの内部動作
ロールバックジャーナル (Rollback Journals)
- SQLiteは基本的に ページを上書きする前にバックアップファイル(ジャーナル)へ保存する
- 問題が発生した場合はジャーナルから復旧して 原子性を保証する
> 注: SQLiteはWALモードもサポートしているが、この記事の範囲では扱わない
スーパージャーナル (Super-Journals)
-
複数のデータベースが接続されている場合、各DBの個別ジャーナルだけでは同期が難しい
-
スーパージャーナルという上位レベルのファイルによって、複数ファイル間のコミットを調整する
-
1つのDBファイル内の複数の仮想テーブルだけを扱う場合は、スーパージャーナルなしでも同期可能
-
いずれの場合でもSQLiteは、トランザクションの流れの中で
xSync、xCommit、xRollbackフックを自動的に呼び出す
仮想テーブルを含む2段階コミット
SQLiteのコミット処理は2段階で行われる:
第1段階: xSync (Durabilityの保証)
- すべてのB-TreeおよびDBファイルのページまたはジャーナルを ディスクへ安全に同期する
- 仮想テーブルでもそれぞれ
xSyncフックが呼び出される - どれか1つの
xSyncで失敗すると、トランザクション全体がロールバックされる → 原子性を維持
第2段階: クリーンアップ (xCommit)
-
ディスクへの保存が完了すると、ジャーナルファイルを削除し、仮想テーブルのクリーンアップを実行する
-
以下は
vdbeaux.cのコードの一部disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); sqlite3VtabCommit(db); -
sqlite3VtabCommit()の内部では、実際には すべてのxCommit呼び出しが失敗しても無視される → 純粋なクリーンアップ段階int sqlite3VtabCommit(sqlite3 *db){ callFinaliser(db, offsetof(sqlite3_module,xCommit)); return SQLITE_OK; } -
xSyncによって耐久性が確保されているため、xCommit、xRollbackは失敗しても無視される
仮想テーブル作成者への注意点
- 永続性を伴う処理は必ず
xSyncに入れるべき- ネットワークI/O、ファイル書き込みなど失敗しうる処理はここで扱うことで、トランザクションを安全に中断できる
xSyncの後でもxRollbackが呼び出される可能性がある- 他のテーブルの
xSyncが失敗すると、全体がロールバックされる
- 他のテーブルの
xCommitとxRollbackは失敗しないクリーンアップ用関数として実装する- **idempotent(冪等)**にし、複数回呼び出されても状態変化がないようにすべき
結論
- SQLiteのジャーナリング機構は、通常テーブルと仮想テーブルを含むすべての要素の原子的コミットを保証する
- 仮想テーブルのトランザクションフックは、SQLiteのトランザクションフローに 自然に統合される
- 仮想テーブルを実装する開発者は、
xSyncに注力してデータ完全性を確保し、クリーンアップ処理はxCommit、xRollbackに分ける必要がある
1件のコメント
Hacker Newsのコメント