principle against Erlang
- Minimize, isolate, and recover from the effects of runtime errors (fault torelance)
- Handle a load increase by adding more hardware resources without changing or redeploying the code (scalability)
- Run your system on multiple machines so that others can take over if one machine crashes (distribution)
それらを達成するために、BEAM VMの処理系が作られてきました。
- BEAMのprocessはOSのprocessではない
- OSのthreadがBEAMのSchedulerを持つ
- BEAMのSchedulerが、BEAMのprocessを管理する
- BEAMの1processの初期メモリは1~2KBなので、OSの1processのMByte単位のメモリ使用量に比べて遥かに小さいし、論理的にはBEAMのprocessは約26,8百万のprocessを実行できる
- BEAMのprocessは、scalabilityを達成するように、資源の追加に対して自動でその資源を使うようになっている
- BEAMのprocessはisolation(分離)している
- メモリは共有しない
- 他のprocessを巻き込んで死なない
- BEAMのprocessは互いにメッセージをやり取りして関係を構築する
BEAMのprocessはconcurrentに動作する。parallelではない。
concurrentに実行することで、複数の処理全体としては高速になるように設計されている
Message passing
メッセージの蓄積をmailboxと呼ぶ。
- mailboxはFIFO。メモリいっぱいまで蓄積される。
- process idに対してメッセージが投げられ、それは共有されない
- Elixirは、
sendとrecieveによりメッセージを送信/受信するrecieveで受信して処理されていないメッセージは、mailboxに溜まり続ける- ずっと蓄積しすぎないために、以下のようなタイムアウトを利用できる
recieve do message -> #処理 other -> #不要な処理 after 5000 -> IO.puts "message not recieved" end
statful/mutable server
上記の receive をloopで処理し続ける機構を容易することで、簡易的なサーバを容易することができる。
(Getting startedでもよくあった例ですね)
この機構は、データを蓄積できるし、状態を持つことができる。
BEAMでは、processをこのように使うことでデータを保持、process間で共有できるようにしている。
このようなサーバとしてのモジュールでは、構造体としてデータを保持するよりもfunctionalなように小さなデータを send / receive できるようにしたほうが良い。
scheduler
BEAMのschedulerの話。
非同期なI/O処理を実施したりするのに変更可能なn個のスレッドを管理したりする。
少し話が脱線します。
Erlang/ElixirではSupervisorの機構で子プロセスの監視、存続を保証しますが、そのおおもとのSupervisorはどう保証されるのだろう?と思ってました。
結局は、だいたいgen_serverの簡単な例とか見ればわかるのですが、ApplicationでSupervisorを起動し始めます。ということは、このSupervisorの親による中央集権的な管理の範囲内でfault torelanceが保たれるのですね。
以下のSASL(System Architecture Support Library)にSupervisor reportなどあるので、多分大きくずれた理解ではなさそう。