Skip to content

Latest commit

 

History

History
268 lines (194 loc) · 10.3 KB

File metadata and controls

268 lines (194 loc) · 10.3 KB

Transaction Control Plan

目的

ACID 特性を一度に全部実装するのではなく、 学習用 RDBMS として理解しやすい最小のトランザクション制御を段階的に導入する。

最初の目標は、単一接続・単一ライタの前提で BEGIN / COMMIT / ROLLBACK、オートコミット、 クラッシュ時に壊れない atomic commit / rollback の最小経路を通すことにある。

現在地

2026-03-22 時点では、architecture.md には Transaction Manager を置く最終像があるが、 実装計画としてはまだ十分に分離されていない。

現在の状態は次の通り。

  • sql crate は最小 CRUD の parser を持つが、BEGIN / COMMIT / ROLLBACK は未対応
  • query crate は AST を正式入力として受ける境界が未整備
  • storage crate には wal.rs の雛形があるが、回復戦略はまだ固定していない
  • 既存文書には「トランザクションは後回し」と「最終像では ACID を扱う」が混在している

この計画では、その間をつなぐ実装順序を固定する。

なぜ最初は rollback journal から始めるか

SQLite の公式文書では、暗黙トランザクションと BEGIN / COMMIT / ROLLBACK を基本のトランザクション制御として説明している。 また、atomic commit の既定方式は rollback journal であり、 WAL は別モードとして後から選べる形になっている。

学習用実装でも、最初から WAL、checkpoint、複数接続、 MVCC、分離レベルをまとめて導入すると責務が広がりすぎる。 そのため、まずは rollback journal で Atomicity と Durability を理解し、 Isolation は単一接続・直列実行で単純化する。

この計画で作るもの

  • sql crate での BEGIN / COMMIT / ROLLBACK の最小 AST と parser
  • query crate で transaction 文を受ける正式入口
  • セッション単位のトランザクション状態
  • オートコミットと明示トランザクションの切り替え
  • rollback journal ベースの commit / rollback / 起動時回復
  • トランザクション文と CRUD を組み合わせた最小テスト

最初に狙う ACID の切り分け

Atomicity

1 つのトランザクションに含まれる変更は、 全部反映されるか、全部取り消されるかのどちらかにする。

Consistency

最初の段階では、型、構文、catalog、ページ不変条件の検証を既存レイヤーで行い、 トランザクション層は「途中状態を永続化しない」ことに集中する。

Isolation

最初は単一接続・単一ライタに限定し、 トランザクション間の干渉を直列実行で避ける。 複数接続、MVCC、分離レベルは後続で扱う。

Durability

COMMIT 成功後の変更は、 クラッシュ後も回復できるよう journal とデータ反映の順序を固定する。

最小スコープ

この計画の第一段階では、次だけを対象にする。

  • SQL は BEGIN, COMMIT, ROLLBACK
  • 実行モデルはオートコミット + 明示トランザクション
  • 同時実行は単一接続、単一ライタ
  • 回復方式は rollback journal
  • 対象文は最小 CRUD と transaction 文の組み合わせ

この段階でやらないこと

  • SAVEPOINT, RELEASE, ROLLBACK TO
  • BEGIN IMMEDIATE, BEGIN EXCLUSIVE などの派生モード
  • 複数接続の同時実行
  • MVCC
  • 分離レベルの選択
  • WAL と checkpoint
  • 分散トランザクション

想定する SQL の最小形

BEGIN;
INSERT INTO users VALUES (1, 'Alice');
UPDATE users SET name = 'Bob' WHERE id = 1;
COMMIT;

BEGIN;
DELETE FROM users WHERE id = 1;
ROLLBACK;

オートコミット時は、BEGIN なしの各文を 暗黙トランザクションとして 1 文ずつ確定させる。

想定する実行モデル

1. オートコミットを既定にする

PostgreSQL と SQLite の公式文書と同様に、 明示的な BEGIN がない場合は各 SQL 文を 1 つのトランザクションとして扱う。

2. 明示トランザクション中は自動確定しない

BEGIN の後は COMMITROLLBACK が来るまで同じトランザクションを継続する。 トランザクション内で BEGIN を再度受けた場合はエラーにする。

3. まずは 1 セッション 1 トランザクション

最初は CLI や rdbms バイナリ上の 1 接続だけを前提にし、 トランザクション状態はセッションにぶら下げる。

想定する回復モデル

rollback journal の基本方針

  • データページを書き換える前に、元の内容を journal へ退避する
  • COMMIT では journal とデータの永続化順序を守る
  • 途中で失敗したら journal から元の状態へ戻す
  • 起動時に未完了 journal が残っていたら rollback してから通常起動する

SQLite の rollback journal も、 元データを別ファイルへ記録してから本体へ書き込むことで atomic commit を実現している。 学習段階ではこの考え方を単純化した最小版から始める。

WAL を後続に送る理由

WAL は reader / writer 並行性と checkpoint を伴うため、 rollback journal より概念が増える。 SQLite の公式文書でも、WAL は rollback journal と別の仕組みとして説明されている。 そのため、最初は rollback journal で commit 順序を理解し、 後から WAL へ広げる。

想定する層構造

flowchart TB
    subgraph SQL["sql crate"]
        P[Parser]
        AST[Statement AST]
    end

    subgraph QUERY["query crate"]
        QB[AST Boundary]
        EX[Executor]
    end

    subgraph TX["transaction layer"]
        TM[Transaction Manager]
        TS[Session Tx State]
    end

    subgraph ST["storage crate"]
        JR[Rollback Journal]
        PG[Page / Buffer / File]
    end

    P --> AST
    AST --> QB
    QB --> EX
    EX --> TM
    TM --> TS
    TM --> JR
    JR --> PG
Loading

実装順序

Step 1. SQL 構文を追加する

  • sql::Statement に transaction 文を追加する
  • parser で BEGIN / COMMIT / ROLLBACK を読めるようにする
  • この段階では派生モードや savepoint は読まない

Step 2. query 境界で transaction 文を通す

  • query crate の正式入口が transaction 文を受け取れるようにする
  • CRUD 文と transaction 文を同じ dispatcher で扱える形にする
  • transaction 文自体は重い planning を要しないので、制御命令として executor へ渡す

Step 3. セッション状態を導入する

  • オートコミット中か、明示トランザクション中かを保持する
  • 二重 BEGINCOMMIT なしの ROLLBACK などを状態エラーとして扱う

Step 4. rollback journal を導入する

  • 書換対象ページの旧イメージを journal に記録する
  • journal の存在で未完了トランザクションを検出できるようにする
  • COMMITROLLBACK の順序をテストで固定する

Step 5. 起動時回復を追加する

  • 起動時に journal が残っていたら rollback を先に走らせる
  • 回復後に通常処理へ進む

Step 6. WAL への拡張余地を整える

  • journal 実装と transaction manager の責務境界を文書で固定する
  • 後から WAL と checkpoint を足せるよう、API を rollback journal に過度に密結合させない

TODO リスト

  • sql crate に transaction 文の AST を追加する
  • parser に BEGIN / COMMIT / ROLLBACK の最小構文を追加する
  • query crate の境界で transaction 文を受け取れるようにする
  • CRUD 文と transaction 文を同じ executor 入口で扱えるようにする
  • セッション単位のトランザクション状態を定義する
  • オートコミット時の 1 文 1 トランザクションを固定する
  • 明示トランザクション中の COMMIT / ROLLBACK を実装する
  • rollback journal のファイル形式または最小記録単位を定義する
  • 書換前ページを journal へ退避してから本体へ反映する順序を固定する
  • 起動時に残留 journal を検出して rollback する回復処理を実装する
  • SAVEPOINT、WAL、複数接続をまだ扱わないことを文書で固定する
  • 将来 WAL へ進む判断基準を docs/decisions.md か後続計画へ残す

テスト方針

  • parser テストで BEGIN / COMMIT / ROLLBACK の正常系と異常系を固定する
  • query crate のテストで transaction 文が AST 境界を越えられることを固定する
  • executor テストでオートコミット時の 1 文確定を固定する
  • 明示トランザクションで複数文を実行し、COMMIT で全反映されることを固定する
  • 明示トランザクションで複数文を実行し、ROLLBACK で無効化されることを固定する
  • 擬似クラッシュ後に journal から回復できることを storage 側のテストで固定する

完了条件

次の状態になれば、この計画の第一段階は完了とみなす。

  • BEGIN / COMMIT / ROLLBACK を parser から executor まで通せる
  • オートコミットと明示トランザクションの両方が動く
  • COMMIT 後の変更が永続化され、ROLLBACK 後の変更が残らない
  • 起動時に未完了 transaction を回復できる
  • rollback journal と transaction manager の責務境界が文書で明確になっている

後続方針

  • SAVEPOINT と部分 rollback を追加する
  • 複数接続を許可する前にロック戦略を決める
  • rollback journal の次段階として WAL と checkpoint を検討する
  • 必要になった段階で MVCC や分離レベルを検討する

参考にした一次情報