$30 off During Our Annual Pro Sale. View Details »

分散システム、本当に「正しく」開発できますか? #JTF2018 / July Tech Festa 2018

分散システム、本当に「正しく」開発できますか? #JTF2018 / July Tech Festa 2018

July Tech Festa 2018 で使用したスライドです。二相コミットを例として、分散アルゴリズムの検証にモデル検査を使用する手法について解説しています。また、代表的なモデル検査ツールである SPIN、TLA+、P について、同じシステムを各ツールで記述してみることでその特定の違いについて学びます。

イベント概要:https://2018.techfesta.jp/speakers#C40
ブログ記事:http://ccvanishing.hateblo.jp/entry/2018/08/01/055608

y_taka_23

July 29, 2018
Tweet

More Decks by y_taka_23

Other Decks in Technology

Transcript

  1. 分散システム、 
    本当に「正しく」
    開発できますか?
    チェシャ猫 (@y_taka_23)
    July Tech Festa 2018 (2018/07/29)

    View Slide

  2. #JTF2018 by @y_taka_23

    View Slide

  3. 目次
    ● 数理的手法による検証とは
    ○ 特にモデル検査の長所と短所
    ● 検証のための理論と枠組み
    ○ システムの挙動をどうやって表現するのか
    ○ 検査したい仕様をどうやって表現するのか
    ● 検証ツール各種の記述方法の比較
    ○ SPIN、TLA+、P それぞれの特徴

    View Slide

  4. 数理的手法の世界
    1

    View Slide

  5. 分散システムはテストしづらい
    ● 各プロセスが非同期的に動く
    ○ 考えるべきパターンが多すぎる
    ● プロセス間で通信が発生
    ○ パケットの遅延・消失・破損が起こりうる
    ● 仕様そのものが複雑になりがち
    ○ 瞬間ではなく、一連の挙動が問題になる
    ○ もし仕様バグがあっても発見しづらい

    View Slide

  6. 人間の直感だけではまともに戦えない世界

    View Slide

  7. 形式手法
    Formal Methods

    View Slide

  8. 形式手法の分類
    ● 定理証明
    ○ 数学的な証明を人の手でプログラムとして表現
    ○ 代表的なツール : Coq、Agda、Isabelle
    ● モデル検査
    ○ システムが取りうる状態を総当たり探索
    ○ 有限のパターンしか保証できないが自動化できる
    ○ 代表的なツール : SPIN、TLA+、P、Alloy

    View Slide

  9. 例:リストの反転

    View Slide

  10. Fixpoint rev (xs : list bool) : list bool :=
    match xs with
    | nil => nil
    | x :: xs’ => rev xs’ ++ (x :: nil)
    end.
    実装とテストしたい仕様

    View Slide

  11. Fixpoint rev (xs : list bool) : list bool :=
    match xs with
    | nil => nil
    | x :: xs’ => rev xs’ ++ (x :: nil)
    end.
    (* rev (rev xs) = xs ? *)
    実装とテストしたい仕様

    View Slide

  12. Fixpoint rev (xs : list bool) : list bool :=
    match xs with
    | nil => nil
    | x :: xs’ => rev xs’ ++ (x :: nil)
    end.
    (* rev (rev xs) = xs ? *)
    rev (rev []) = []
    rev (rev [true]) = [true]
    rev (rev [true; false]) = [true; false]
    rev (rev [true; true; false]) = [true; true; false]
    ...
    実装とテストしたい仕様

    View Slide

  13. 定理証明の考え方

    View Slide

  14. Theorem rev_involutive :
    forall xs : list bool, rev (rev xs) = xs.
    Proof.
    Qed.
    数学的帰納法による証明

    View Slide

  15. Theorem rev_involutive :
    forall xs : list bool, rev (rev xs) = xs.
    Proof.
    intro.
    induction xs [| x xs’ xs’_is_ok].
    simpl.
    apply eq_refl.
    Qed.
    数学的帰納法による証明
    xs = nil の場合、
    直接計算すれば = が成り立つ

    View Slide

  16. Theorem rev_involutive :
    forall xs : list bool, rev (rev xs) = xs.
    Proof.
    intro.
    induction xs [| x xs’ xs’_is_ok].
    simpl.
    apply eq_refl.
    simpl.
    rewrite rev_app_distr.
    simpl.
    rewrite xs’_is_ok.
    apply eq_refl.
    Qed.
    数学的帰納法による証明
    xs = x :: xs’ の場合、
    xs’ では = が成り立つとして
    式変形を行う

    View Slide

  17. モデル検査の考え方

    View Slide

  18. 長さ 3 以下の範囲で自動全探索

    View Slide

  19. []
    [true]
    [false]
    [true; true]
    [true; false]
    [false; true]
    [false; false]
    [true; true; true]
    [true; true; false]
    [true; false; true]
    [false; true; true]
    [true; false; false]
    [false; true; false]
    [false; false; true]
    [false; false; false]
    長さ 3 以下の範囲で自動全探索

    View Slide

  20. rev (rev [])
    rev (rev [true])
    rev (rev [false])
    rev (rev [true; true])
    rev (rev [true; false])
    rev (rev [false; true])
    rev (rev [false; false])
    rev (rev [true; true; true])
    rev (rev [true; true; false])
    rev (rev [true; false; true])
    rev (rev [false; true; true])
    rev (rev [true; false; false])
    rev (rev [false; true; false])
    rev (rev [false; false; true])
    rev (rev [false; false; false])
    長さ 3 以下の範囲で自動全探索

    View Slide

  21. rev (rev []) = []
    rev (rev [true]) = [true]
    rev (rev [false]) = [false]
    rev (rev [true; true]) = [true; true]
    rev (rev [true; false]) = [true; false]
    rev (rev [false; true]) = [false; true]
    rev (rev [false; false]) = [false; false]
    rev (rev [true; true; true]) = [true; true; true]
    rev (rev [true; true; false]) = [true; true; false]
    rev (rev [true; false; true]) = [true; false; true]
    rev (rev [false; true; true]) = [false; true; true]
    rev (rev [true; false; false]) = [true; false; false]
    rev (rev [false; true; false]) = [false; true; false]
    rev (rev [false; false; true]) = [false; false; true]
    rev (rev [false; false; false]) = [false; false; false]
    長さ 3 以下の範囲で自動全探索

    View Slide

  22. (たぶん)rev (rev xs) = xs
    長さ 3 以下の範囲で自動全探索

    View Slide

  23. 第 1 章のまとめ
    ● 分散システムには特有の難しさがある
    ○ 考えるべきパターンが多く、再現性がない
    ● 形式手法による検証は大きく分けて二種類
    ○ 定理証明:手動・無限のパターンに対して保証
    ○ モデル検査:自動・特定の範囲に絞り全探索
    ● 今回はモデル検査に焦点を当てる
    ○ 分散システムの状態を「上手く」探索したい

    View Slide

  24. オートマトンと検査
    2

    View Slide

  25. 例:二相コミット

    View Slide

  26. Transaction
    Mamager
    Resource
    Mamager-1
    Resource
    Manager-2
    Resource
    Manager-3

    View Slide

  27. 二相コミットのアルゴリズム (1)
    ● TM (Transaction Manager) の視点
    ○ RM から prepared 報告を受け取ったら記録する
    ○ RM に対し Commit 命令または Abort 命令を出す
    ○ Commit 命令は全 RM からの prepared 報告後のみ
    ○ Abort 命令はいつでも出せる
    ○ 命令はブロードキャストで、相手 RM は選べない
    ○ RM との通信は到達に時間がかかる

    View Slide

  28. 二相コミットのアルゴリズム (2)
    ● RM (Resource Manager) の視点
    ○ 他の RM とは一切通信できない
    ○ 変更をコミットしたい時は TM に prepared を報告
    ○ コミット前に突然 aborted 状態になりうる
    ○ TM からのCommit 命令を受け取ると変更をコミット
    ○ TM からの Abort 命令を受け取ると変更を破棄
    ○ TM との通信は到達に時間がかかる

    View Slide

  29. なるほどわからん。

    View Slide

  30. TM
    RM1
    RM1 : --
    RM2 : --
    RM3 : --
    RM2
    RM3

    View Slide

  31. TM
    RM1
    RM1 : --
    RM2 : --
    RM3 : --
    prepared
    RM2
    RM3

    View Slide

  32. TM
    RM1
    RM1 : prepared
    RM2 : --
    RM3 : --
    prepared
    RM2
    RM3

    View Slide

  33. TM
    RM1
    RM1 : prepared
    RM2 : --
    RM3 : prepared
    prepared
    prepared
    RM2
    RM3

    View Slide

  34. TM
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    prepared
    prepared
    prepared
    RM2
    RM3

    View Slide

  35. TM
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    prepared
    prepared
    prepared
    RM2
    RM3

    View Slide

  36. TM : Commit
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    prepared
    prepared
    prepared
    RM2
    RM3

    View Slide

  37. TM : Commit
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    prepared
    committed
    prepared
    RM2
    RM3

    View Slide

  38. TM : Commit
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    prepared
    committed
    committed
    RM2
    RM3

    View Slide

  39. TM : Commit
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    committed
    committed
    committed
    RM2
    RM3

    View Slide

  40. TM : Commit
    RM1
    RM1 : prepared
    RM2 : prepared
    RM3 : prepared
    committed
    prepared
    aborted
    RM2
    RM3
    inconsistency!

    View Slide

  41. もっと見通しのいい表現が欲しい

    View Slide

  42. オートマトンとしてのプロセス
    ● 時間を離散化して考える
    ● 状態と遷移でプロセスを記述
    ○ 状態:その時点で変数に格納された値一式
    ○ 遷移:プログラムの実行ステップ
    ● 例えば RM をオートマトンにすると
    ○ 状態:working, prepared, committed, aborted
    ○ 遷移 : TM への送信、TM からの受信

    View Slide

  43. working
    aborted
    committed
    prepared

    View Slide

  44. RM が二つ同時に動いていると

    View Slide

  45. RM1: working
    RM2: working
    RM1: working
    RM2: prepared
    RM1: aborted
    RM2: working
    RM1: working
    RM2: aborted
    RM1: prepared
    RM2: working

    View Slide

  46. RM1: working
    RM2: working
    RM1: working
    RM2: prepared
    RM1: aborted
    RM2: working
    RM1: working
    RM2: aborted
    RM1: prepared
    RM2: working
    RM1 : working -> prepared

    View Slide

  47. RM1: working
    RM2: working
    RM1: working
    RM2: prepared
    RM1: aborted
    RM2: working
    RM1: working
    RM2: aborted
    RM1: prepared
    RM2: working
    RM2 : working -> aborted

    View Slide

  48. オートマトンの非同期積
    ● 二つのオートマトンを並列に動かす
    ○ 分散システムの「妥当な」形式化になっている
    ● 積も再びオートマトンになる
    ○ 状態:両方のオートマトンの状態の組
    ○ 遷移:どちらか片方のオートマトンの遷移
    ● システムをモジュール化して扱える
    ○ 部分を次々と合成することで全体の挙動が得られる

    View Slide

  49. TM: init
    RM1: working
    RM2: working
    RM3: working
    TM: init
    RM1: prepared
    RM2: working
    RM3: working
    TM: committed
    RM1: committed
    RM2: working
    RM3: aborted

    View Slide

  50. TM: init
    RM1: working
    RM2: working
    RM3: working
    TM: init
    RM1: prepared
    RM2: working
    RM3: working
    TM: committed
    RM1: committed
    RM2: working
    RM3: aborted

    View Slide

  51. TM: init
    RM1: working
    RM2: working
    RM3: working
    TM: init
    RM1: prepared
    RM2: working
    RM3: working
    TM: committed
    RM1: committed
    RM2: working
    RM3: aborted

    View Slide

  52. 後は幅優先探索するだけ...か?

    View Slide

  53. 仕様:A した後に B が成り立つ

    View Slide

  54. Time
    A

    View Slide

  55. Time
    A
    B

    View Slide

  56. Time
    A
    B

    View Slide

  57. Time
    A
    B B B

    View Slide

  58. 仕様を正確に表現できる言語が必要

    View Slide

  59. 線形時相論理(LTL)
    ● 通常の論理式に記号を追加した体系
    ○ [] A : その時点以降、常に A が成立
    ○ <> A : その時点以降、いつかは A が成立
    ○ X A : その時点のすぐ次の時点で A が成立
    ● 状態の「列」に対して真偽を判定する
    ○ システムの実行パスに対して条件が記述できる
    ● Büchi オートマトンに変換して検査できる

    View Slide

  60. Time
    A
    B
    [](A => <>B)

    View Slide

  61. Time
    A
    B
    [](A => X B)

    View Slide

  62. Time
    A
    B B B
    [](A => X ([]B))

    View Slide

  63. 第 2 章のまとめ
    ● オートマトンによるシステム記述
    ○ それぞれのプロセスをオートマトンで表す
    ○ さらにその非同期積を取る
    ○ 積も一つのオートマトンになる
    ○ 有効グラフの探索問題として定式化できる
    ● 時相論理によってあいまい性を排除
    ○ 瞬間ではなく一連の挙動を定義できる

    View Slide

  64. 検証ツール比較
    3

    View Slide

  65. SPIN
    https://spinroot.com

    View Slide

  66. SPIN の特徴
    ● C 言語風の文法でプロセスを記述
    ○ オートマトンを意識する必要性はあまりない
    ○ ただしデータ型などはだいぶ貧弱
    ● チャンネルによる通信をサポート
    ○ 完全な FIFO 型の通信に特化
    ● 検証専用の C 言語コードを出力
    ○ スケール性能を性能を追求

    View Slide

  67. mtype = { PREPARED, COMMIT, ABORT };
    bool committed[3];
    bool aborted[3];
    proctype TM(...) {
    ...
    }
    proctype RM(...) {
    ...
    }
    init {
    ...
    }
    SPIN による記述(全体)

    View Slide

  68. mtype = { PREPARED, COMMIT, ABORT };
    bool committed[3];
    bool aborted[3];
    proctype TM(...) {
    ...
    }
    proctype RM(...) {
    ...
    }
    init {
    ...
    }
    SPIN による記述(全体)
    通信のタイプ

    View Slide

  69. mtype = { PREPARED, COMMIT, ABORT };
    bool committed[3];
    bool aborted[3];
    proctype TM(...) {
    ...
    }
    proctype RM(...) {
    ...
    }
    init {
    ...
    }
    SPIN による記述(全体)
    グローバルな条件

    View Slide

  70. mtype = { PREPARED, COMMIT, ABORT };
    bool committed[3];
    bool aborted[3];
    proctype TM(...) {
    ...
    }
    proctype RM(...) {
    ...
    }
    init {
    ...
    }
    SPIN による記述(全体)
    プロセスの定義

    View Slide

  71. mtype = { PREPARED, COMMIT, ABORT };
    bool committed[3];
    bool aborted[3];
    proctype TM(...) {
    ...
    }
    proctype RM(...) {
    ...
    }
    init {
    ...
    }
    SPIN による記述(全体)
    初期化シーケンス

    View Slide

  72. init {
    chan id_gen = [3] of { byte };
    chan rm_to_tm = [3] of { mtype, byte };
    chan tm_to_rm1 = [1] of { mtype };
    chan tm_to_rm2 = [1] of { mtype };
    chan tm_to_rm3 = [1] of { mtype };
    atomic {
    id_gen ! 0;
    id_gen ! 1;
    id_gen ! 2;
    run TM(rm_to_tm, tm_to_rm1, tm_to_rm2, tm_to_rm3);
    run RM(rm_to_tm, tm_to_rm1, id_gen);
    run RM(rm_to_tm, tm_to_rm2, id_gen);
    run RM(rm_to_tm, tm_to_rm3, id_gen);
    }
    }
    SPIN による記述(初期化処理)

    View Slide

  73. init {
    chan id_gen = [3] of { byte };
    chan rm_to_tm = [3] of { mtype, byte };
    chan tm_to_rm1 = [1] of { mtype };
    chan tm_to_rm2 = [1] of { mtype };
    chan tm_to_rm3 = [1] of { mtype };
    atomic {
    id_gen ! 0;
    id_gen ! 1;
    id_gen ! 2;
    run TM(rm_to_tm, tm_to_rm1, tm_to_rm2, tm_to_rm3);
    run RM(rm_to_tm, tm_to_rm1, id_gen);
    run RM(rm_to_tm, tm_to_rm2, id_gen);
    run RM(rm_to_tm, tm_to_rm3, id_gen);
    }
    }
    SPIN による記述(初期化処理)
    チャンネルの生成

    View Slide

  74. init {
    chan id_gen = [3] of { byte };
    chan rm_to_tm = [3] of { mtype, byte };
    chan tm_to_rm1 = [1] of { mtype };
    chan tm_to_rm2 = [1] of { mtype };
    chan tm_to_rm3 = [1] of { mtype };
    atomic {
    id_gen ! 0;
    id_gen ! 1;
    id_gen ! 2;
    run TM(rm_to_tm, tm_to_rm1, tm_to_rm2, tm_to_rm3);
    run RM(rm_to_tm, tm_to_rm1, id_gen);
    run RM(rm_to_tm, tm_to_rm2, id_gen);
    run RM(rm_to_tm, tm_to_rm3, id_gen);
    }
    }
    SPIN による記述(初期化処理)
    不可分実行

    View Slide

  75. init {
    chan id_gen = [3] of { byte };
    chan rm_to_tm = [3] of { mtype, byte };
    chan tm_to_rm1 = [1] of { mtype };
    chan tm_to_rm2 = [1] of { mtype };
    chan tm_to_rm3 = [1] of { mtype };
    atomic {
    id_gen ! 0;
    id_gen ! 1;
    id_gen ! 2;
    run TM(rm_to_tm, tm_to_rm1, tm_to_rm2, tm_to_rm3);
    run RM(rm_to_tm, tm_to_rm1, id_gen);
    run RM(rm_to_tm, tm_to_rm2, id_gen);
    run RM(rm_to_tm, tm_to_rm3, id_gen);
    }
    }
    SPIN による記述(初期化処理)
    プロセスの生成

    View Slide

  76. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)

    View Slide

  77. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)
    非決定的に選択
    = オートマトンが分岐

    View Slide

  78. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)
    チャンネルからの受信

    View Slide

  79. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)
    prepared が揃っているか

    View Slide

  80. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)
    Commit 命令を一斉送信

    View Slide

  81. proctype TM(chan ch_from, ch_to1, ch_to2, ch_to3) {
    byte id;
    bool prepared[3];
    end: do
    :: ch_from ? PREPARED(id) -> prepared[id] = true;
    :: prepared[0] && prepared[1] && prepared[2] ->
    ch_to1 ! COMMIT;
    ch_to2 ! COMMIT;
    ch_to3 ! COMMIT;
    :: ch_to1 ! ABORT;
    ch_to2 ! ABORT;
    ch_to3 ! ABORT;
    od
    }
    SPIN による記述(TM)
    Abort 命令を一斉送信

    View Slide

  82. proctype RM(chan ch_to, ch_from, id_gen) {
    byte id;
    id_gen ? id;
    if
    :: ch_to ! PREPARED(id) ->
    if
    :: ch_from ? COMMIT ->
    committed[id] = true;
    :: ch_from ? ABORT ->
    aborted[id] = true;
    fi
    :: aborted[id] = true;
    fi
    }
    SPIN による記述(RM)

    View Slide

  83. proctype RM(chan ch_to, ch_from, id_gen) {
    byte id;
    id_gen ? id;
    if
    :: ch_to ! PREPARED(id) ->
    if
    :: ch_from ? COMMIT ->
    committed[id] = true;
    :: ch_from ? ABORT ->
    aborted[id] = true;
    fi
    :: aborted[id] = true;
    fi
    }
    SPIN による記述(RM)
    自分の id を TM に送信

    View Slide

  84. proctype RM(chan ch_to, ch_from, id_gen) {
    byte id;
    id_gen ? id;
    if
    :: ch_to ! PREPARED(id) ->
    if
    :: ch_from ? COMMIT ->
    committed[id] = true;
    :: ch_from ? ABORT ->
    aborted[id] = true;
    fi
    :: aborted[id] = true;
    fi
    }
    SPIN による記述(RM)
    受信結果で対応が変わる

    View Slide

  85. TLA+
    https://lamport.azumewebsites.net/tla/tla.html

    View Slide

  86. TLA+ の特徴
    ● 生のオートマトンでシステムを記述
    ○ 状態遷移の条件を直接記述
    ○ プロセスごとではなく全体をまとめて記述する
    ● プログラム的な記述は DSL (+CAL) でサポート
    ○ 自動でアルゴリズムをオートマトンに変換
    ● GUI 環境が非常によく整っている

    View Slide

  87. CONSTANT RM
    VARIABLES rmState, tmState, tmPrepare, msgs
    Message ==
    [type : {“Prepared”}, rm : RM] \union
    [type : {“Commit”, “Abort”}]
    Init ==
    /\ rmState = [rm \in RM |-> “working”]
    /\ tmState = “init”
    /\ tmPrepare = {}
    /\ msgs = {}
    TLA+ による記述(全体)

    View Slide

  88. CONSTANT RM
    VARIABLES rmState, tmState, tmPrepare, msgs
    Message ==
    [type : {“Prepared”}, rm : RM] \union
    [type : {“Commit”, “Abort”}]
    Init ==
    /\ rmState = [rm \in RM |-> “working”]
    /\ tmState = “init”
    /\ tmPrepare = {}
    /\ msgs = {}
    TLA+ による記述(全体)
    (個数を限定しない)RM の集合

    View Slide

  89. CONSTANT RM
    VARIABLES rmState, tmState, tmPrepare, msgs
    Message ==
    [type : {“Prepared”}, rm : RM] \union
    [type : {“Commit”, “Abort”}]
    Init ==
    /\ rmState = [rm \in RM |-> “working”]
    /\ tmState = “init”
    /\ tmPrepare = {}
    /\ msgs = {}
    TLA+ による記述(全体)
    状態はすべてグローバル

    View Slide

  90. CONSTANT RM
    VARIABLES rmState, tmState, tmPrepare, msgs
    Message ==
    [type : {“Prepared”}, rm : RM] \union
    [type : {“Commit”, “Abort”}]
    Init ==
    /\ rmState = [rm \in RM |-> “working”]
    /\ tmState = “init”
    /\ tmPrepare = {}
    /\ msgs = {}
    TLA+ による記述(全体)
    初期状態

    View Slide

  91. Next ==
    /\ TMCommit
    /\ TMAbort
    /\ \E rm \in RM :
    \/ TMRcvPrepared(rm)
    \/ RMPrepare(rm)
    \/ RMChooseToAbort(rm)
    \/ RMRcvCommitMsg(rm)
    \/ RMRcvAbortMsg(rm)
    TLA+ による記述(全体)

    View Slide

  92. Next ==
    /\ TMCommit
    /\ TMAbort
    /\ \E rm \in RM :
    \/ TMRcvPrepared(rm)
    \/ RMPrepare(rm)
    \/ RMChooseToAbort(rm)
    \/ RMRcvCommitMsg(rm)
    \/ RMRcvAbortMsg(rm)
    TLA+ による記述(全体)
    可能な遷移の全体

    View Slide

  93. TMRcvPrepared(rm) ==
    /\ tmState = “init”
    /\ [type |-> “Prepared”, rm |-> rm] \in msgs
    /\ tmPrepared’ = tmPrepared \union {rm}
    /\ UNCHANGED <>
    TMCommit ==
    /\ rmState = “init”
    /\ tmPrepared = RM
    /\ rmState’ = “committed”
    /\ msgs’ = msgs \union {[type |-> “Commit”]}
    /\ UNAHGEND <>
    TMAbort == ...
    TLA+ による記述(TM)

    View Slide

  94. TMRcvPrepared(rm) ==
    /\ tmState = “init”
    /\ [type |-> “Prepared”, rm |-> rm] \in msgs
    /\ tmPrepared’ = tmPrepared \union {rm}
    /\ UNCHANGED <>
    TMCommit ==
    /\ rmState = “init”
    /\ tmPrepared = RM
    /\ rmState’ = “committed”
    /\ msgs’ = msgs \union {[type |-> “Commit”]}
    /\ UNAHGEND <>
    TMAbort == ...
    TLA+ による記述(TM)
    TM が Commit 命令を送信

    View Slide

  95. TMRcvPrepared(rm) ==
    /\ tmState = “init”
    /\ [type |-> “Prepared”, rm |-> rm] \in msgs
    /\ tmPrepared’ = tmPrepared \union {rm}
    /\ UNCHANGED <>
    TMCommit ==
    /\ rmState = “init”
    /\ tmPrepared = RM
    /\ rmState’ = “committed”
    /\ msgs’ = msgs \union {[type |-> “Commit”]}
    /\ UNAHGEND <>
    TMAbort == ...
    TLA+ による記述(TM)
    「‘」をつけると遷移先を意味する

    View Slide

  96. RMPrepare(rm) ==
    /\ rmState[rm] = “working”
    /\ rmState’ = [rmState EXCEPT ![rm] = “prepared”]
    /\ msgs’ = msgs \union
    {[type |-> “Prepared”, rm |-> rm]}
    /\ UNCHANGED <>
    RMChooseAbort(rm) == ...
    RMRcvCommitMsg(rm) ==
    /\ [type |-> “Commit”] \in msgs
    /\ rmState’ = [rmState EXCEPT ![rm] = “committed”]
    /\ UNCHANGED <>
    RMRcvAbortMsg(rm) == ...
    TLA+ による記述(RM)

    View Slide

  97. P
    https://github.com/p-org/P

    View Slide

  98. P 言語の特徴
    ● 状態とハンドラの組でプロセスを記述
    ○ Web フレームワークの Controller に近い
    ● 各プロセスが入力用のキューを持つ
    ○ SPIN と異なりキューはプロセスの持ち物
    ○ 他のプロセスからのメッセージがキューに積まれる
    ● 実行可能な C 言語のコードを出力

    View Slide

  99. event PREPARE: machine;
    event COMMIT;
    event ABORT;
    machine TM {
    ...
    }
    machine RM {
    ...
    }
    P による記述(全体)

    View Slide

  100. event PREPARE: machine;
    event COMMIT;
    event ABORT;
    machine TM {
    ...
    }
    machine RM {
    ...
    }
    P による記述(全体)
    メッセージの種類

    View Slide

  101. event PREPARE: machine;
    event COMMIT;
    event ABORT;
    machine TM {
    ...
    }
    machine RM {
    ...
    }
    P による記述(全体)
    プロセスの定義

    View Slide

  102. machine TM {
    var rms: seq[machine];
    var prepared: map[machine, bool];
    start state Init {
    on PREPARE (rm: machine) {
    prepared[rm] = true;
    if (sizeof(prepared) == sizeof(rms)) {
    SendCommitMsg();
    goto Committed;
    }
    }
    }
    state Committed {}
    state Aborted {}
    }
    P による記述(TM)

    View Slide

  103. machine TM {
    var rms: seq[machine];
    var prepared: map[machine, bool];
    start state Init {
    on PREPARE (rm: machine) {
    prepared[rm] = true;
    if (sizeof(prepared) == sizeof(rms)) {
    SendCommitMsg();
    goto Committed;
    }
    }
    }
    state Committed {}
    state Aborted {}
    }
    P による記述(TM)
    状態の定義

    View Slide

  104. machine TM {
    var rms: seq[machine];
    var prepared: map[machine, bool];
    start state Init {
    on PREPARE (rm: machine) {
    prepared[rm] = true;
    if (sizeof(prepared) == sizeof(rms)) {
    SendCommitMsg();
    goto Committed;
    }
    }
    }
    state Committed {}
    state Aborted {}
    }
    P による記述(TM)
    PREPARE メッセージをデキュー

    View Slide

  105. machine TM {
    var rms: seq[machine];
    var prepared: map[machine, bool];
    start state Init {
    on PREPARE (rm: machine) {
    prepared[rm] = true;
    if (sizeof(prepared) == sizeof(rms)) {
    SendCommitMsg();
    goto Committed;
    }
    }
    }
    state Committed {}
    state Aborted {}
    }
    P による記述(TM)
    関数を実行して次の状態へ

    View Slide

  106. machine TM {
    ...
    start state Init { ... }
    state Committed {}
    state Aborted {}
    fun SendCommitMsg() {
    var i: int;
    i = 0;
    while (i < sizeof(rms)) {
    send rms[i], COMMIT;
    i = i + 1;
    }
    }
    }
    P による記述(TM)

    View Slide

  107. machine TM {
    ...
    start state Init { ... }
    state Committed {}
    state Aborted {}
    fun SendCommitMsg() {
    var i: int;
    i = 0;
    while (i < sizeof(rms)) {
    send rms[i], COMMIT;
    i = i + 1;
    }
    }
    }
    P による記述(TM)
    すべての RM に COMMIT 命令

    View Slide

  108. machine RM {
    start state Working { ... }
    state Prepare { ... }
    state Committed {}
    state Aborted {}
    }
    P による記述(RM)

    View Slide

  109. machine RM {
    start state Working {
    entry (tm: machine) {
    if ($) {
    send tm, PREPARE, this;
    goto Prepare;
    } else {
    goto Aborted;
    }
    }
    on Commit {
    goto Committed;
    }
    }
    }
    P による記述(RM)

    View Slide

  110. machine RM {
    start state Working {
    entry (tm: machine) {
    if ($) {
    send tm, PREPARE, this;
    goto Prepare;
    } else {
    goto Aborted;
    }
    }
    on Commit {
    goto Committed;
    }
    }
    }
    P による記述(RM)
    非決定的な真偽値
    = オートマトンの分岐

    View Slide

  111. machine RM {
    start state Working {
    entry (tm: machine) {
    if ($) {
    send tm, PREPARE, this;
    goto Prepare;
    } else {
    goto Aborted;
    }
    }
    on Commit {
    goto Committed;
    }
    }
    }
    P による記述(RM)
    メッセージが送信され、
    TM のキューに積まれる

    View Slide

  112. LTL による検査のサポート
    ● SPIN
    ○ 可能だが、検査したい式の否定を入力する必要あり
    ● TLA+
    ○ 完全なサポート、システム定義側でも使用可
    ● P 言語
    ○ サポートなし、検査したい内容を machine で表現
    ○ 場合によっては複雑な変換が必要になる

    View Slide

  113. 第 3 章のまとめ
    ● SPIN
    ○ チャンネルを介して通信するプロセスを定義
    ● TLA+
    ○ システム全体の状態遷移の条件を明示的に定義
    ○ +CAL を使用すると手続き的な記述もできる
    ● P 言語
    ○ 各状態とイベントに対するリアクションを定義

    View Slide

  114. 本日のまとめ
    4

    View Slide

  115. 本日のまとめ
    ● 分散システムの検証に形式手法が有効
    ○ 考えるパターンが多く人の手では扱えない
    ● オートマトンを基礎としたモデル検査
    ○ 形式化・抽象化による体系的な状態の列挙
    ○ 時相論理により、表現しづらい仕様を明示
    ● 各モデル検査ツールの特徴
    ○ どれもクセがあるのでシステムの性質に合わせて選ぶ

    View Slide

  116. Cool Head, and Formal Heart.
    Presented by チェシャ猫 (@y_taka_23)

    View Slide