Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

#JTF2018 by @y_taka_23

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

数理的手法の世界 1

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

形式手法 Formal Methods

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

例:リストの反転

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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] ... 実装とテストしたい仕様

Slide 13

Slide 13 text

定理証明の考え方

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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 の場合、 直接計算すれば = が成り立つ

Slide 16

Slide 16 text

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’ では = が成り立つとして 式変形を行う

Slide 17

Slide 17 text

モデル検査の考え方

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

[] [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 以下の範囲で自動全探索

Slide 20

Slide 20 text

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 以下の範囲で自動全探索

Slide 21

Slide 21 text

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 以下の範囲で自動全探索

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

オートマトンと検査 2

Slide 25

Slide 25 text

例:二相コミット

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

なるほどわからん。

Slide 30

Slide 30 text

TM RM1 RM1 : -- RM2 : -- RM3 : -- RM2 RM3

Slide 31

Slide 31 text

TM RM1 RM1 : -- RM2 : -- RM3 : -- prepared RM2 RM3

Slide 32

Slide 32 text

TM RM1 RM1 : prepared RM2 : -- RM3 : -- prepared RM2 RM3

Slide 33

Slide 33 text

TM RM1 RM1 : prepared RM2 : -- RM3 : prepared prepared prepared RM2 RM3

Slide 34

Slide 34 text

TM RM1 RM1 : prepared RM2 : prepared RM3 : prepared prepared prepared prepared RM2 RM3

Slide 35

Slide 35 text

TM RM1 RM1 : prepared RM2 : prepared RM3 : prepared prepared prepared prepared RM2 RM3

Slide 36

Slide 36 text

TM : Commit RM1 RM1 : prepared RM2 : prepared RM3 : prepared prepared prepared prepared RM2 RM3

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

working aborted committed prepared

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

RM1: working RM2: working RM1: working RM2: prepared RM1: aborted RM2: working RM1: working RM2: aborted RM1: prepared RM2: working

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Time A

Slide 55

Slide 55 text

Time A B

Slide 56

Slide 56 text

Time A B

Slide 57

Slide 57 text

Time A B B B

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

検証ツール比較 3

Slide 65

Slide 65 text

SPIN https://spinroot.com

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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 による記述(初期化処理)

Slide 73

Slide 73 text

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 による記述(初期化処理) チャンネルの生成

Slide 74

Slide 74 text

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 による記述(初期化処理) 不可分実行

Slide 75

Slide 75 text

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 による記述(初期化処理) プロセスの生成

Slide 76

Slide 76 text

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)

Slide 77

Slide 77 text

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) 非決定的に選択 = オートマトンが分岐

Slide 78

Slide 78 text

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) チャンネルからの受信

Slide 79

Slide 79 text

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 が揃っているか

Slide 80

Slide 80 text

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 命令を一斉送信

Slide 81

Slide 81 text

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 命令を一斉送信

Slide 82

Slide 82 text

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)

Slide 83

Slide 83 text

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 に送信

Slide 84

Slide 84 text

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) 受信結果で対応が変わる

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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+ による記述(全体)

Slide 88

Slide 88 text

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 の集合

Slide 89

Slide 89 text

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+ による記述(全体) 状態はすべてグローバル

Slide 90

Slide 90 text

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+ による記述(全体) 初期状態

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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)

Slide 94

Slide 94 text

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 命令を送信

Slide 95

Slide 95 text

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) 「‘」をつけると遷移先を意味する

Slide 96

Slide 96 text

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)

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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)

Slide 103

Slide 103 text

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) 状態の定義

Slide 104

Slide 104 text

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 メッセージをデキュー

Slide 105

Slide 105 text

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) 関数を実行して次の状態へ

Slide 106

Slide 106 text

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)

Slide 107

Slide 107 text

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 命令

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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 のキューに積まれる

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

本日のまとめ 4

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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