Joe Armstrong の博士論文をサラッと読んだ

Closed 作成 更新 8 comments
dak2

バグは必ず起きる

「バグのないソフトウェアを書く」ではなく、バグは必ず残るのを前提にしている。 問いは「壊れても全体として動き続けるシステムをどう作るか」。 信頼性を正しさではなく、壊れ方の設計として扱っている。

絶対に壊れてはいけない小さな核(error kernel)だけを堅く作り、 それ以外は落ちてもいい前提。

dak2

骨子

論文は、並行指向の言語が満たすべき条件をこう並べる。

  • プロセスがある
  • プロセス同士は強く隔離されている
  • プロセスごとに一意で偽造できない ID がある
  • 状態を共有しない
  • やりとりは届く保証のないメッセージだけ
  • 他のプロセスの死を検知できる
dak2

Let it crash と supervisor

ここでいうプロセスは OS プロセスではなく、BEAM 上の軽量プロセス。 ヒープも GC も個別なので、1 つが落ちても他は巻き込まれない。

エラーは落ちたプロセス自身ではなく、外のプロセスが対処する。

  • supervisor と子プロセスは link でつながっていて、子プロセスが死ぬと通知が飛ぶ
  • supervisor は trap_exit で、その通知をメッセージとして受け取る
  • 再起動のしかたは戦略で決める(one_for_one / one_for_all / rest_for_one)
  • 短時間に再起動しすぎたら supervisor 自身が死に、親に任せる
dak2

OS プロセスと Erlang プロセス

「プロセス」が 2 つの意味で出てくるので整理。

OS プロセス(BEAM = Erlang VM)   ← 1 個。ps で見えるのはこれ
 ├─ OS スレッド(スケジューラ)× コア数
 ├─ Erlang プロセス A             ← 主体
 ├─ Erlang プロセス B             ← 主体
 └─ Erlang プロセス C ...         ← 主体(何万個でも)
  • 主体は Erlang プロセス。Erlang プロセスの中にさらに主体がいるわけではなく、1 個 = 1 本の逐次的な流れ
  • supervisor も OS プロセスではなく、ただの Erlang プロセス
  • 同じ OS プロセスの中にいるので、アドレス空間は共有している。でもヒープは別々で、他のプロセスのヒープを指す手段が言語にない
  • メッセージ送信は、受け手の mailbox に値をコピーするだけ。システムコールもソケットもない
  • 隔離を守っているのは OS ではなく言語と VM。なので C で書いた NIF が segfault すると、OS プロセスごと全部死ぬ
dak2

link と trap_exit

supervisor が子プロセスの死をどう知るか。

  • link は 2 つのプロセスを生死でつなぐ双方向のリンク。片方が死ぬと、もう片方に exit シグナルが飛ぶ
  • 普通のプロセスは、相手が異常終了したときの exit シグナルを受けると一緒に死ぬ(正常終了 normal なら死なない)
  • supervisor は process_flag(trap_exit, true) を立てているので、一緒に死なずに {'EXIT', Pid, Reason} というメッセージとして受け取る
  • 定期的に生死を見に行くのではなく、死んだことが届くだけ

受け取ったあとは、子ごとの設定で再起動するかを決める。

  • permanent:どんな理由で終わっても再起動
  • transient:異常終了のときだけ再起動
  • temporary:再起動しない

link はノードもまたげる。別ノードとの接続が切れると、理由が noconnection の通知が届く。ただ、相手が死んだのかネットワークが切れただけなのかは区別できない。

dak2

その他おもしろかったところ

  • ホットコードスワップ:モジュールの新旧 2 バージョンが共存し、?MODULE:loop() を通った時点でプロセスが新しいコードに乗り換える。
  • behaviour:並行処理とエラー処理という難しい部分は OTP が持ち、アプリ側は逐次的なコールバックを書くだけ
dak2

感想

  • Erlang/OTP の設計思想知らなかったので興味深い
  • Alan Kay が OOP の核心として言っていた「メッセージング」を一番素直にやっていると思った
    • Erlang は軽くて隔離されたプロセスが設計の基本単位で、プロセス同士は非同期メッセージでやりとり。
  • 事例の章(AXD301)はまだちゃんと読めていないので、そのうち

このスクラップはクローズされています

← Notes 一覧