Upgrade to Pro — share decks privately, control downloads, hide ads and more …

テスト駆動開発から証明駆動開発へ #JTF2019 / July Tech Festa 2019

y_taka_23
December 08, 2019

テスト駆動開発から証明駆動開発へ #JTF2019 / July Tech Festa 2019

July Tech Festa 2019 で使用したスライドです。

近年、テストを書く文化は広く普及しており、開発フローにおいて自動テストを組み込むことはもはや常識となりました。しかしよく考えてみると、有限個のテストケースが保証しているのは、所詮「特定の有限個の入力に対する出力」にしか過ぎません。では「あり得る全ての入力」に対してプログラムの性質を保証することは果たして可能でしょうか? この問いに対する答えのひとつが「定理証明」と呼ばれる手法です。定理証明では、数学的な「証明」をプログラム上でエンコードすることにより、真に「全ての入力」を扱うことができます。本セッションではこの定理証明を取り上げ、従来のテストとの考え方の違いや具体的な適用方法について、サンプルを交えつつ解説します。

イベント概要:https://2019.techfesta.jp/speakers#A10

y_taka_23

December 08, 2019
Tweet

More Decks by y_taka_23

Other Decks in Technology

Transcript

  1. #JTF2019 #JTF2019_A
    Proof.
    #JTF2019 #JTF2019_A
    テスト駆動開発から証明駆動開発へ
    チェシャ猫 (@y_taka_23)
    July Tech Festa 2019 (2019/12/08)

    View full-size slide

  2. #JTF2019 #JTF2019_A
    本日のアジェンダ
    ● 分散システムとテスト
    ● プログラムと証明の間には
    ● 定理証明と数学的帰納法
    ● 分散システムの証明

    View full-size slide

  3. #JTF2019 #JTF2019_A
    分散システムとテスト
    Lemma 1.

    View full-size slide

  4. #JTF2019 #JTF2019_A
    Kubernetes
    https://github.com/kubernetes/kubernetes

    View full-size slide

  5. #JTF2019 #JTF2019_A
    etcd
    https://github.com/etcd-io/etcd

    View full-size slide

  6. #JTF2019 #JTF2019_A
    CloudNative の立役者 etcd
    ● Kubernetes のバックエンド KVS
    ○ システム全体の Source of Truth
    ○ 壊れると Kubernetes が動作不能に
    ● 分散合意アルゴリズム Raft を利用
    ○ クラスタの過半数が生存していれば大丈夫
    ○ Paxos と比較して理解可能性を重視

    View full-size slide

  7. #JTF2019 #JTF2019_A
    枯れたクラスタリング技術?

    View full-size slide

  8. #JTF2019 #JTF2019_A
    etcd v3.4 (2019/08) での修正点
    ● 新ノードの Join 時に問題が発生
    ○ データコピーの際、Leader が高負荷で死ぬ
    ○ 起動失敗の場合でもメンバとして認識してしまう
    ○ 1-Node クラスタへの Join に失敗すると即崩壊
    ● 新しい状態 Learner の導入
    ○ Join 直後は「過半数」にカウントされない

    View full-size slide

  9. #JTF2019 #JTF2019_A
    意外と問題があったりする

    View full-size slide

  10. #JTF2019 #JTF2019_A
    考えるべき事象が多すぎる
    ● 通信パケットが入れ替わったら?
    ● 通信パケットが重複して送信されたら?
    ● 通信パケットが欠落したら?
    ● 変なタイミングでノードが死んだら?
    ● 実は生きていて通信が遅れて届いたら?

    View full-size slide

  11. #JTF2019 #JTF2019_A
    分散システムの検証困難性
    ● 各ノードが非同期に動作する
    ○ 考えるべきパターンが多すぎる
    ● ノードやネットワークは信頼できない
    ○ 独立にランダムなタイミングで故障が発生
    ● 仕様そのものが複雑になりがち
    ○ 特定の瞬間ではなく一連の動作が問題に

    View full-size slide

  12. #JTF2019 #JTF2019_A
    これテスト書くの辛くない?

    View full-size slide

  13. #JTF2019 #JTF2019_A
    古典的なテストは「未知」に弱い
    ● 人間がテストケースを与える
    ○ 明示した有限個のケースしか保証できない
    ○ 思いつかない異常系はそもそも考慮外
    ● 再現性がある問題しか扱えない
    ○ 「たまに落ちるテスト」はあまり役に立たない
    ○ 特にタイミングに依存する問題は難しい

    View full-size slide

  14. #JTF2019 #JTF2019_A
    「未知」に対するふたつのアプローチ

    View full-size slide

  15. #JTF2019 #JTF2019_A
    1. 未知に素早く反応する

    View full-size slide

  16. #JTF2019 #JTF2019_A
    Chaos Engineering
    ● 運用環境であえて障害を発生させる
    ○ サービス不能状態に陥らないか
    ○ 障害状態から正しく復旧できるか
    ● 隠れていた問題点を炙り出せる
    ○ 意図して発生させることが難しいバグを発見
    ○ いわば「未知に素早く反応する」アプローチ

    View full-size slide

  17. #JTF2019 #JTF2019_A
    Section 1 のポイント
    ● 流行りの分散システムは留意点が多い
    ○ タイミング・組み合わせ・ランダム性
    ● 古典的なテストの限界
    ○ 既知の問題から外れたケースの保証は苦手
    ● 未知の事象にどう対処するか
    ○ Chaos Engineering は「未知に素早く反応」する

    View full-size slide

  18. #JTF2019 #JTF2019_A
    2. 未知を既知にする

    View full-size slide

  19. #JTF2019 #JTF2019_A
    #JTF2019 #JTF2019_A
    形式手法
    Formal Methods

    View full-size slide

  20. #JTF2019 #JTF2019_A
    プログラムと証明の間には
    Lemma 2.

    View full-size slide

  21. #JTF2019 #JTF2019_A
    形式手法とは
    ● システムを数学的対象により表現
    ○ その対象の性質に基づいて検証を行う
    ○ 対象として何を選ぶかでツールの性質が決まる
    ○ テストケースの抜けや漏れが生じない
    ● 厳密化した仕様を機械的にチェック
    ○ いわば「既知の範囲を押し拡げる」アプローチ

    View full-size slide

  22. #JTF2019 #JTF2019_A
    形式手法の分類
    ● モデル検査
    ○ システムが取りうる値を列挙して探索
    ○ 有限個のパターンに収まれば自動化できる
    ● 定理証明(今日のテーマ)
    ○ いわゆる数学的な証明をプログラム的に表現
    ○ 真に無限個のパターンを扱うことができる

    View full-size slide

  23. #JTF2019 #JTF2019_A
    型、付けてますか?

    View full-size slide

  24. #JTF2019 #JTF2019_A
    静的型による安全性
    ● 型は値の不変条件を表す
    ○ 実行中に int 型が string 型に変わったりしない
    ○ 指定した関数・メソッドの存在を保証
    ● 型システムは言語によって色々
    ○ 豊かな型システムは複雑な条件を強制できる
    ○ 多くの場合、数学的な定式化が与えられている

    View full-size slide

  25. #JTF2019 #JTF2019_A
    直積型
    ● ふたつの型をペアにする
    ○ 長さが型レベルで固定されている
    ○ 異なる種類の型の値をまとめられる
    # let pi = ("pi", 3.14);;
    val pi : string * float = ("pi", 3.14)
    # let pi2 = ["pi", 3.14];;
    #=> Type Error

    View full-size slide

  26. #JTF2019 #JTF2019_A
    直和型
    ● ふたつの型のどちらか片方を持つ
    ○ パターンマッチでもれなく場合分けできる
    type result =
    | Success of int
    | Error of string
    fun handle r =
    match r with
    | Success val -> do_something val
    | Error msg -> show_message msg

    View full-size slide

  27. #JTF2019 #JTF2019_A
    関数型
    ● 関数にも値と同じ扱いで型が付く
    ○ 型を引数にとる高階関数も型で守られる
    ○ あらかじめ最初の引数のみ与えたりもできる
    # let plus x y = x + y;;
    val plus : int -> int -> int =
    # let answer = plus 42;;
    val answer : int -> int =

    View full-size slide

  28. #JTF2019 #JTF2019_A
    プログラムの世界
    ● P 型と Q 型の直積型
    ○ P 型と Q 型の両方の値が手に入る
    ● P 型と Q 型の直和型
    ○ P 型か Q 型の値の少なくとも片方は手に入る
    ● P 型から Q 型への関数型
    ○ P 型の値を受け取って Q 型の値を作り出す

    View full-size slide

  29. #JTF2019 #JTF2019_A
    本日のテーマは定理証明

    View full-size slide

  30. #JTF2019 #JTF2019_A
    証明の世界
    ● P かつ Q
    ○ P と Q の両方の証明が手に入る
    ● P または Q
    ○ P か Q の証明の少なくとも片方は手に入る
    ● P ならば Q
    ○ P の証明を受け取って Q の証明が作り出す

    View full-size slide

  31. #JTF2019 #JTF2019_A
    プログラムの世界(再掲)
    ● P 型と Q 型の直積型
    ○ P 型と Q 型の両方の値が手に入る
    ● P 型と Q 型の直和型
    ○ P 型か Q 型の値の少なくとも片方は手に入る
    ● P 型から Q 型への関数型
    ○ P 型の値を受け取って Q 型の値を作り出す

    View full-size slide

  32. #JTF2019 #JTF2019_A
    こいつら何か似てる!

    View full-size slide

  33. #JTF2019 #JTF2019_A
    Curry-Howard 同型対応
    証明の世界 プログラムの世界
    命題 型
    命題 P の証明 P 型の値を得るプログラム
    P かつ Q P と Q の直積型
    P または Q P と Q の直和型
    P ならば Q P から Q への関数型

    View full-size slide

  34. #JTF2019 #JTF2019_A
    もうちょっと実例が欲しい

    View full-size slide

  35. #JTF2019 #JTF2019_A
    例:三段論法
    ● 証明の世界
    ○ 前提:ソクラテスならば人間、人間ならば死ぬ、
    ○ 帰結:ソクラテスならば死ぬ
    ● プログラムの世界
    ○ 実装済み:f : double -> int、g : int -> string
    ○ 関数合成:x => g (f x) : double -> string

    View full-size slide

  36. #JTF2019 #JTF2019_A
    例:Modus Ponens
    ● 証明の世界
    ○ 前提:P ならば Q である、P は成り立つ
    ○ 帰結:Q は成り立つ
    ● プログラムの世界
    ○ 実装済み:f : int -> string、x : int
    ○ 関数適用:f x : string

    View full-size slide

  37. #JTF2019 #JTF2019_A
    Section 2 のポイント
    ● 形式手法とは
    ○ システムを数学的な対象にマッピング
    ○ 大きく分けてモデル検査と定理証明がある
    ○ 静的型付き言語も広い意味で形式手法
    ● 命題とプログラムの型とが対応する
    ○ 証明をプログラムにエンコードして扱える

    View full-size slide

  38. #JTF2019 #JTF2019_A
    定理証明と数学的帰納法
    Lemma 3.

    View full-size slide

  39. #JTF2019 #JTF2019_A
    証明、大学受験以来なんだけど

    View full-size slide

  40. #JTF2019 #JTF2019_A
    例:0 から n までの和の公式

    View full-size slide

  41. #JTF2019 #JTF2019_A
    数学的帰納法
    ● 証明を 2 ステップに分割
    ○ 出発点となるケースについて成立を示す
    ○ 任意の場所の「一歩手前」まで成立したと仮定
    ○ その仮定の下で「一歩先」での成立を示す
    ● 無限性のある主張が証明できる
    ○ 直接計算で「全ての自然数 n について」は不可能

    View full-size slide

  42. #JTF2019 #JTF2019_A
    n = 0 の場合

    View full-size slide

  43. #JTF2019 #JTF2019_A
    n = 0 の場合
    直接計算で成り立つ

    View full-size slide

  44. #JTF2019 #JTF2019_A
    n = n’ + 1 の場合
    仮定:
    結論:

    View full-size slide

  45. #JTF2019 #JTF2019_A
    n = n’ + 1 の場合
    仮定:
    結論:
    仮定を利用

    View full-size slide

  46. #JTF2019 #JTF2019_A
    n = n’ + 1 の場合
    仮定:
    結論:
    等式変形

    View full-size slide

  47. #JTF2019 #JTF2019_A
    n’ に対する命題の証明から
    n’ + 1 に対する命題の証明を構成

    View full-size slide

  48. #JTF2019 #JTF2019_A
    n’ に対するプログラムから
    n’ + 1 に対するプログラムを構成

    View full-size slide

  49. #JTF2019 #JTF2019_A
    Curry-Howard 同型対応ふたたび
    証明の世界 プログラムの世界
    命題 型
    命題 P の証明 P 型の値を得るプログラム
    P かつ Q P と Q の直積型
    P または Q P と Q の直和型
    P ならば Q P から Q への関数型
    数学的帰納法 再帰関数

    View full-size slide

  50. #JTF2019 #JTF2019_A
    Coq
    https://github.com/coq/coq

    View full-size slide

  51. #JTF2019 #JTF2019_A

    View full-size slide

  52. #JTF2019 #JTF2019_A
    Coq で和を計算する関数
    Inductive nat : Type :=
    | O : nat
    | S : nat -> nat.
    Fixpoint sum_upto (n : nat) : nat :=
    match n with
    | O => 0
    | S n’ => n + sum_upto n’
    end.
    Compute (sum_upto 100). (* 5050 *)

    View full-size slide

  53. #JTF2019 #JTF2019_A
    Coq で和を計算する関数
    Inductive nat : Type :=
    | O : nat
    | S : nat -> nat.
    Fixpoint sum_upto (n : nat) : nat :=
    match n with
    | O => 0
    | S n’ => n + sum_upto n’
    end.
    Compute (sum_upto 100). (* 5050 *)
    自然数を直和型で定義

    View full-size slide

  54. #JTF2019 #JTF2019_A
    Coq で和を計算する関数
    Inductive nat : Type :=
    | O : nat
    | S : nat -> nat.
    Fixpoint sum_upto (n : nat) : nat :=
    match n with
    | O => 0
    | S n’ => n + sum_upto n’
    end.
    Compute (sum_upto 100). (* 5050 *)
    パターンマッチして
    再帰

    View full-size slide

  55. #JTF2019 #JTF2019_A
    Coq で証明すべき定理
    Theorem sum_formula :
    forall n : nat, 2 * (sum_upto n) = n * S n.
    Proof.
    intro n.
    induction [| n’ H_n’ ]
    - (* n = 0 の場合の証明 *)
    ...
    - (* n = S n’ の場合の証明 *)
    ...
    Qed.

    View full-size slide

  56. #JTF2019 #JTF2019_A
    Coq で証明すべき定理
    Theorem sum_formula :
    forall n : nat, 2 * (sum_upto n) = n * S n.
    Proof.
    intro n.
    induction [| n’ H_n’ ]
    - (* n = 0 の場合の証明 *)
    ...
    - (* n = S n’ の場合の証明 *)
    ...
    Qed.
    パターンマッチに
    よる場合分け

    View full-size slide

  57. #JTF2019 #JTF2019_A
    実際に証明して見せてよ?

    View full-size slide

  58. #JTF2019 #JTF2019_A
    (ライブ・プルービング)

    View full-size slide

  59. #JTF2019 #JTF2019_A
    Program Extraction
    ● Coq 単体では I/O などが記述できない
    ○ 関数の性質を証明しても実装に組み込めない
    ● 通常の言語のコードに変換できる
    ○ 重要な関数のみ証明し、変換して使用する
    ○ デフォルトでは OCaml、Haskell、Scheme
    ○ プラグインで拡張することも可能

    View full-size slide

  60. #JTF2019 #JTF2019_A
    Section 3 のポイント
    ● 数学的帰納法の利用
    ○ プログラム的には再帰関数として実現できる
    ● Coq による対話的証明
    ○ 書き換えによりゴールを引き寄せて進む
    ● Extraction によるコード生成
    ○ 重要な関数を証明してプロダクトに埋め込む

    View full-size slide

  61. #JTF2019 #JTF2019_A
    分散システムの証明
    Lemma 4.

    View full-size slide

  62. #JTF2019 #JTF2019_A
    数学っぽいのじゃなくて
    「プログラムの証明」が知りたい

    View full-size slide

  63. #JTF2019 #JTF2019_A
    例:分散ロックサーバ
    ● サーバとエージェントが存在
    ○ エージェントは LockMsg / UnlockMsg を要求
    ○ サーバは LockMsg を先着順で処理
    ○ ロックを与える場合は GrantMsg を送信
    ● 二重ロックを防ぎたい
    ○ 非同期リクエストでも正しく処理できるか?

    View full-size slide

  64. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client

    View full-size slide

  65. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    Lock
    LockMsg

    View full-size slide

  66. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    LockMsg
    Lock

    View full-size slide

  67. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    GrantMsg
    Grant

    View full-size slide

  68. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    Unlock
    UnlockMsg

    View full-size slide

  69. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    GrantMsg
    Grant

    View full-size slide

  70. #JTF2019 #JTF2019_A
    これゼロから書くの辛くない?

    View full-size slide

  71. #JTF2019 #JTF2019_A
    Verdi
    https://github.com/uwplse/verdi

    View full-size slide

  72. #JTF2019 #JTF2019_A
    証明フレームワーク Verdi
    ● Coq 用のフレームワーク
    ○ 分散システムを体系的に証明できる
    ○ Extraction してランタイムに組み込み可能
    ● ユーザが定義するシステムの動作
    ○ ノード、外部入出力、内部メッセージ
    ○ 外部入力、内部メッセージに対するハンドラ

    View full-size slide

  73. #JTF2019 #JTF2019_A
    Server
    Agent (A1)
    Other
    Process
    Agent (A2)
    Other
    Process
    Client
    Client
    外部入出力 内部メッセージ

    View full-size slide

  74. #JTF2019 #JTF2019_A
    外部入力ハンドラ(エージェント)
    HandleInp (n: Name) (s: State n) (inp: Inp) :=
    match n with
    | Server => nop
    | Agent n =>
    match inp with
    | Lock =>
    send (Server, LockMsg)
    | Unlock =>
    if s == true then (
    s := false;;
    send (Server, UnlockMsg))

    View full-size slide

  75. #JTF2019 #JTF2019_A
    外部入力ハンドラ(エージェント)
    HandleInp (n: Name) (s: State n) (inp: Inp) :=
    match n with
    | Server => nop
    | Agent n =>
    match inp with
    | Lock =>
    send (Server, LockMsg)
    | Unlock =>
    if s == true then (
    s := false;;
    send (Server, UnlockMsg))
    Lock が来たら
    サーバに LockMsg

    View full-size slide

  76. #JTF2019 #JTF2019_A
    外部入力ハンドラ(エージェント)
    HandleInp (n: Name) (s: State n) (inp: Inp) :=
    match n with
    | Server => nop
    | Agent n =>
    match inp with
    | Lock =>
    send (Server, LockMsg)
    | Unlock =>
    if s == true then (
    s := false;;
    send (Server, UnlockMsg))
    Unlock が来たら
    自分の持つフラグを下ろして
    サーバに UnlockMsg

    View full-size slide

  77. #JTF2019 #JTF2019_A
    メッセージハンドラ(サーバ)
    HandleMsg (n: Name) (s: State n)
    (src: Name)(msg: Msg) :=
    match n with
    | Server => nop
    match msg with
    | LockMsg =>
    if s == [] then send (src, GrantMsg);;
    s := s ++ [src]
    | UnlockMsg =>
    s := tail s;;
    if s != [] then send (head s, GrantMsg)

    View full-size slide

  78. #JTF2019 #JTF2019_A
    メッセージハンドラ(サーバ)
    HandleMsg (n: Name) (s: State n)
    (src: Name)(msg: Msg) :=
    match n with
    | Server => nop
    match msg with
    | LockMsg =>
    if s == [] then send (src, GrantMsg);;
    s := s ++ [src]
    | UnlockMsg =>
    s := tail s;;
    if s != [] then send (head s, GrantMsg)
    リストの先頭が
    ロック保持中のエージェント

    View full-size slide

  79. #JTF2019 #JTF2019_A
    メッセージハンドラ(エージェント)
    HandleMsg (n: Name) (s: State n)
    (src: Name)(msg: Msg) :=
    match n with
    | Agent n =>
    match msg with
    | GrantMsg =>
    s := true;;
    output Grant

    View full-size slide

  80. #JTF2019 #JTF2019_A
    メッセージハンドラ(エージェント)
    HandleMsg (n: Name) (s: State n)
    (src: Name)(msg: Msg) :=
    match n with
    | Agent n =>
    match msg with
    | GrantMsg =>
    s := true;;
    output Grant
    サーバから返事が来たら
    自分の持つフラグを立てて
    外部システムに Grant 通知

    View full-size slide

  81. #JTF2019 #JTF2019_A
    ノードの動作はわかった。証明は?

    View full-size slide

  82. #JTF2019 #JTF2019_A
    Verdi によるシステムの定式化
    ● 三つ組 (Σ, P, T) で「世界」を定義
    ○ Σ : 各ノードが持つ内部状態
    ○ P : 通信路の途中にいるパケットの多重集合
    ○ T : 外部入出力イベントの列(トレース)
    ● 世界間の到達可能関係を考える
    ○ システムの挙動はグラフ構造として表される

    View full-size slide

  83. #JTF2019 #JTF2019_A
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg), (A2, LockMsg)}
    T = [ , ]
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg) }
    T = [ ]
    Σ = { Server : [], A1: false, A2: false }
    P = {}
    T = []

    View full-size slide

  84. #JTF2019 #JTF2019_A
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg), (A2, LockMsg)}
    T = [ , ]
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg) }
    T = [ ]
    Σ = { Server : [], A1: false, A2: false }
    P = {}
    T = []
    A1 が Lock を要求

    View full-size slide

  85. #JTF2019 #JTF2019_A
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg), (A2, LockMsg)}
    T = [ , ]
    Σ = { Server : [], A1: false, A2: false }
    P = { (A1, LockMsg) }
    T = [ ]
    Σ = { Server : [], A1: false, A2: false }
    P = {}
    T = []
    A2 が Lock を要求

    View full-size slide

  86. #JTF2019 #JTF2019_A
    Σ = { Server : [A1, A2], A1: false, A2: false }
    P = {}
    T = [ , , ]
    Σ = { Server : [A1], A1: true, A2: false }
    P = { (A2, LockMsg) }
    T = [ , , ]
    Σ = { Server : [A1], A1: false, A2: false }
    P = { (A1, GrantMsg), (A2, LockMsg) }
    T = [ , ]
    Server が A1 を Grant

    View full-size slide

  87. #JTF2019 #JTF2019_A
    Σ = { Server : [A1, A2], A1: false, A2: false }
    P = {}
    T = [ , , ]
    Σ = { Server : [A1], A1: true, A2: false }
    P = { (A2, LockMsg) }
    T = [ , , ]
    Σ = { Server : [A1], A1: false, A2: false }
    P = { (A1, GrantMsg), (A2, LockMsg) }
    T = [ , ]
    A1 がGrant を受信

    View full-size slide

  88. #JTF2019 #JTF2019_A
    Σ = { Server : [A1, A2], A1: false, A2: false }
    P = {}
    T = [ , , ]
    Σ = { Server : [A1], A1: true, A2: false }
    P = { (A2, LockMsg) }
    T = [ , , ]
    Σ = { Server : [A1], A1: false, A2: false }
    P = { (A1, GrantMsg), (A2, LockMsg) }
    T = [ , ]
    Server が A2 の要求を認識

    View full-size slide

  89. #JTF2019 #JTF2019_A
    Σ = { Server : [A2], A1: false, A2: true }
    P = { () }
    T = [ , , , , ]
    Σ = { Server : [A2], A1: false, A2: false }
    P = { (A2, GrantMsg) }
    T = [ , , , ]
    Σ = { Server : [A1, A2], A1: true, A2: false }
    P = { (A1, UnlockMsg) }
    T = [ , , , ]
    A1 が Unlock を要求

    View full-size slide

  90. #JTF2019 #JTF2019_A
    Σ = { Server : [A2], A1: false, A2: true }
    P = { () }
    T = [ , , , , ]
    Σ = { Server : [A2], A1: false, A2: false }
    P = { (A2, GrantMsg) }
    T = [ , , , ]
    Σ = { Server : [A1, A2], A1: true, A2: false }
    P = { (A1, UnlockMsg) }
    T = [ , , , ]
    Server が A2 を Grant

    View full-size slide

  91. #JTF2019 #JTF2019_A
    Σ = { Server : [A2], A1: false, A2: true }
    P = { () }
    T = [ , , , , ]
    Σ = { Server : [A2], A1: false, A2: false }
    P = { (A2, GrantMsg) }
    T = [ , , , ]
    Σ = { Server : [A1, A2], A1: true, A2: false }
    P = { (A1, UnlockMsg) }
    T = [ , , , ]
    A2 が Grant を受信

    View full-size slide

  92. #JTF2019 #JTF2019_A
    実際に何が証明できる?

    View full-size slide

  93. #JTF2019 #JTF2019_A
    Verdi による仕様記述
    ● トレース T に対して仕様を定義
    ○ システムの内部メッセージには言及しない
    ● 例:二重ロックしない
    ○ 任意の a1, a2 を考えたとき、
    T = t1 ++ [] ++ t2 ++ [] ++ t3
    の形で表せるなら、t2 は を含む

    View full-size slide

  94. #JTF2019 #JTF2019_A
    Verdi による証明
    ● 世界間の遷移に対して数学的帰納法
    ○ 初期状態の世界で仕様が成立
    ○ ある世界で仕様が成立すると仮定したとき、
    そこから 1 ステップ進んだすべての世界で成立
    仮定
    World
    World
    World
    World
    証明
    証明
    証明

    View full-size slide

  95. #JTF2019 #JTF2019_A
    形式手法の分類(再掲)
    ● モデル検査
    ○ システムが取りうる値を列挙して探索
    ○ 有限個のパターンに収まれば自動化できる
    ● 定理証明
    ○ いわゆる数学的な証明をプログラム的に表現
    ○ 真に無限個のパターンを扱うことができる

    View full-size slide

  96. #JTF2019 #JTF2019_A
    参考:モデル検査の場合
    ● 世界の遷移が作るグラフに対して探索
    ○ 条件を満たさない T を持つ世界を探す
    ○ 世界が有限個になるようにモデル化する必要
    Initial
    World
    World
    World
    World
    World World
    T が不正
    World
    World
    World
    World

    View full-size slide

  97. #JTF2019 #JTF2019_A
    分散システムの不安定さの話は?

    View full-size slide

  98. #JTF2019 #JTF2019_A
    例:メッセージ重複への対処
    ● Verdi が自動でラッパを適用
    ○ メッセージにユニークな ID 番号を振る
    ○ 各ノードに「すでに読んだ ID」を記憶させる
    ○ すでに読んだ ID のメッセージは無視する
    ● 二重ロック禁止の仕様は守れるか?
    ○ 重複なしの場合の証明が成り立たない可能性

    View full-size slide

  99. #JTF2019 #JTF2019_A
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    重複ありの場合 重複なしの場合

    View full-size slide

  100. #JTF2019 #JTF2019_A
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    Σ = { Server : []
    A1: false
    A2: false }
    P = {
    (A1, LockMsg),
    }
    T = [ ..., ]
    重複ありの場合 重複なしの場合
    unwrap はトレース T を保存

    View full-size slide

  101. #JTF2019 #JTF2019_A
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    Σ = { Server : [], read: { 000, … }
    A1: false, read: { 001, … }
    A2: false, read: { 002, … } }
    P = {
    (042, A1, LockMsg),
    (042, A1, LockMsg),
    }
    T = [ ..., ]
    Σ = { Server : []
    A1: false
    A2: false }
    P = {
    (A1, LockMsg),
    }
    T = [ ..., ]
    重複ありの場合 重複なしの場合
    unwrap 先でも
    対応する(自明な)
    遷移が存在

    View full-size slide

  102. #JTF2019 #JTF2019_A
    Initial World
    重複ありの場合 重複なしの場合
    証明済:
    到達可能な任意の世界で
    トレース T は条件を満たす
    World
    World World
    Initial World
    World
    World World World

    View full-size slide

  103. #JTF2019 #JTF2019_A
    Initial World
    重複ありの場合 重複なしの場合
    証明済:
    到達可能な任意の世界で
    トレース T は条件を満たす
    World
    World World
    Initial World
    World
    World World World
    対応する遷移が存在 =
    ラップした状態でも
    到達可能性は変わらない

    View full-size slide

  104. #JTF2019 #JTF2019_A
    Initial World
    重複ありの場合 重複なしの場合
    証明済:
    到達可能な任意の世界で
    トレース T は条件を満たす
    World
    World World
    Initial World
    World
    World World World
    実は読み替え可能:
    到達可能な任意の世界で
    トレース T は条件を満たす

    View full-size slide

  105. #JTF2019 #JTF2019_A
    Verified System Transformer
    ● より複雑な意味論への変換
    ○ メッセージとハンドラを自動でラップ
    ○ 故障への対応について改めて考える必要がない
    ● 実装だけでなく証明も変換可能
    ○ 意味論の間にある模倣関係を利用
    ○ 仕様をトレースに制限したのが効いている

    View full-size slide

  106. #JTF2019 #JTF2019_A
    故障を含む Verdi の意味論(一部)
    ● Duplicating Semantics
    ○ メッセージが通信路上で複製される
    ● Dropping Semantics
    ○ メッセージが消失、タイムアウトする
    ● Node Failure
    ○ ノードが落ちたり、勝手に復活したりする

    View full-size slide

  107. #JTF2019 #JTF2019_A
    vard: 証明された Raft ベース KVS
    https://github.com/uwplse/verdi-raft

    View full-size slide

  108. #JTF2019 #JTF2019_A
    Section 4 のポイント
    ● Verdi による分散システムの証明
    ○ Runtime とセットで実行可能コードを生成
    ● システムのトレースで仕様を表現
    ○ 複雑に見えるが本質は数学的帰納法
    ● Verified System Transformer
    ○ 故障に対応したバージョンに変換できる

    View full-size slide

  109. #JTF2019 #JTF2019_A
    本日のまとめ
    Theorem.

    View full-size slide

  110. #JTF2019 #JTF2019_A
    本日のまとめ
    ● 形式手法による検証
    ○ テストでは扱いづらい分散システムにも使える
    ● Coq による定理証明
    ○ プログラムと証明との Curry-Howard 対応
    ● 証明フレームワーク Verdi
    ○ 分散システムの証明が可能、Raft も証明済み

    View full-size slide

  111. #JTF2019 #JTF2019_A
    おまけ:モデル検査について
    Corollary.

    View full-size slide

  112. #JTF2019 #JTF2019_A
    【PR】分散システム + モデル検査
    https://speakerdeck.com/ytaka23/builderscon-tokyo-2019

    View full-size slide

  113. #JTF2019 #JTF2019_A
    【PR】モデル検査ハンズオン
    ハッシュタグ「#モデル検査ハンズオン」で検索

    View full-size slide

  114. #JTF2019 #JTF2019_A
    Qed.
    #JTF2019 #JTF2019_A
    Prove Your Distributed Systems!
    Presented By チェシャ猫 (@y_taka_23)

    View full-size slide