[Elixir in Action]fault toleranceを保つための特別な責務を負うprocess~Supervisor~

BEAMの真骨頂であるFault toleranceに関して、複数章に渡って説明しているところの話。 ここではその触りの箇所。 fault torelanceであるようにするためには、failureを知り、それに対してリカバリして現状の機能を提供し続けることが最低限必要です。そのためにBEAMがどのような機構を備えて、Elixir/Erlangではどうなのか?をまとめています。 3つのタイプのエラー BEAMでは3つの種類のerrorを持ちます。 errors ** [ArithmeticError] bad argument in arithmetic expression などで意図せずエラーがでるとき exits exit(“I’m done”) のように、意図的にプロセスを終了させる throw throw(:throw_value) のように、意図的にthrowを投げる throwの目的は、non-localな相手へ何か情報を与える時に使うことです。(BEAMでは、remote processに何か処理を任せる、ということが多々ありますね) Elixirでは、すべてのエラーは 必ず 何らかの値を返します。 そのため、任意のエラーに対して共通で何かする、というときは以下のように _ を使う必要があります。 それ以外では、 catch のパターンマッチを使い、特定のエラーの時は意図した処理を行うような処理を書きます。 defexception により、任意のmacroを組むことが可能。 もし、エラー処理を用意していない場合、 processはterminate されます。制御できないエラーはどんどん落としてしまえ、というやつですね。 errors in concurrent system BEAMではprocessで分離しているので、何らかのprocessのエラーが他processへ影響を与えることはありません。 そのため、processを監視して、互いを把握していく必要があります。 Link Process.link/1 や spawn_link/1 を使うことで、2つの異なるprocessをリンクします。リンクするとは、一方のプロセスが死んだら、もう一方はそれを知ることができるし、その逆も然り、という相互の関係を持つことを指します。 正常にexitするときは :normal という値を返します。それ以外は、基本的に異常終了である、と判断します。 通常、終了のシグナルは以下のメッセージを構成します。 何らかのエラーを投げて死ぬ場合は、以下の形式のメッセージを構成します。…More

[Elixir in Action]OTP/GenServerを学んで非同期/並行処理を学ぶ

Elixir in Actionの続き。ようやっと3分の2くらい。写経もほどほどに実施しながらだとこのくらいですかね。 Chatpter6、7ではOpen Telecom Platformと、その上に構成されるGenServerに関する内容をざーっと通してました。 自前のloopで作っていた簡易サーバから、GenServerへと一般化、どのように処理しているのか?をボトルネックを解消していくという話の流れのなかで説明していっています。 サーバの基本的な役割は以下 Spawn でプロセスを分ける loopでプロセスを回す processの状態を管理する messageに反応する 送信元に応答を返す(送る) 最終的にはGenServerに近づいていくので、大きくここら辺は割愛。 ただ、 非常に考え方は重要 だし、ここら辺がErlang/Elixirのアーキテクチャとして選ぶ価値があると判断される箇所だと思うので、ちゃんと理解する方が良さそうです。 Elixir/Erlangのサーバで重要な役割を持つのは以下。 gen_server init, handle_call, handle_castなどのcallbacksを持つ 想定していない処理を無視するために、 def handle_info(_, state), do: something といった処理も入れる superevisor errorハンドリングやリカバリを担う application gen_event gen_fsm single processで処理される時のボトルネックは、多くのリクエストが溜まるにつれて応答(process)が遅くなること。 そこで、concurrentに処理できるように拡張する。ただ、cncurrentに処理できるように spawn により別プロセスで処理可能にすると、同期的に処理したい時が複雑になる。そこで、Elixir/Erlangでは以下のように GenServer.reply を使い、うまいこと非同期処理を利用している。 single processでリクエストを操作し、その実際の処理は子プロセスに実施させる。必ずこの方法が良いというわけではないが、本書ではこの方法を解の1つとして紹介していた。 ボトルネックの話でいうと、databaseを相手にしはじめると、DBとアプリをつなぐ間のpoolを処理するためのプロセスも関係してきますが、そこら辺はEctoでは poolboy が役割を担っているそうな。 poolboy Getting Startedを読んでなくていきなりこれでは辛いけれど、Getting Startedの後なら特にconcurrencyやfault toleranceの話を知る上ではこれは読んで価値ありそうです。特に、Erlang/Elixirに限らず、concurrencyやfault toleranceの考え方は参考になると思います。 補足コード Process.registerは以下…More

[Elixir in Action]BEAMとしてのprincipleとその実現のための処理系

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を達成するように、資源の追加に対して自動でその資源を使うようになっている…More

[Elixir in Action]polymorphismで拡張していく

Data abstractionsに書かれている章でした。 ここが終わったので、いよいよElixir/Erlangの真骨頂であるHigh Availabilityなシステムを構築するための説明に入ります。 structure moduleは、抽象データを作るために使われる。 map structs mapとstructsは同じように使われることが多い。 ただ、違うところもある。 例えばパターンマッチ このように、structはmapの特殊な形として使われる。 ちなみに、Recordというモジュールもあって、これは主にErlangライブラリを使う時には、Recordをimportして使いましょう、というものらしい。 polymorphism Elixirでは、polymorphismは protocol によって実現される。 defprotocol ではインターフェースを定義して、 defimpl でその実装を加えていく。 例えば、 to_string を独自のmodule、 Sample に加えるなら、 とすると、その結果として と得られる。(IO.putsはString.Chars.to_stringを指している) このprotocolは、他にも Inspect や Access といったものもある。 Elixirでは、このように元となる protocol に対して defimpl 内の実装を増やすことで機能を拡張していく。More

[Elixir in Action]Guard clauseの優先度

control flowの章の、 [Elixir in Action]Erlang/Elixirの再帰計算におけるnon-tail recursionとtail recursion 以外のことを。 以下のように、文字列バイナリも左辺/右辺のマッチングによって分けることができます。 以下のように、連続したマッチングもできます。 以下のように書いた場合、パターンマッチングでは上から順にマッチングを試みます。そのため a(other) が最も先頭にきたらそれがマッチングされます。 Elixirでは、以下のようなGuard clauseによっても適用される関数を条件付けすることができます。この時、 when で使われる型には優先度が存在します。 優先度は以下。 つまり、上記の Sample.a/1 に対してatomの :not_a_number を適用した場合、 が適用されます。エラーはありません。 そのため、数字だけを受け付けるようにするために以下のように書きます。 これで、 :not_a_number のようなものは FunctionClauseError がraiseされるようになります。 Cmprehensionsは、 for x <- [list], do: {何か} で構成される。 into などの要素を使ったりすると、繰り返しをより簡潔にかけたりする。なるほど。 Streamの説明もありましたが、Stream、色々できそうですね。 File.stream! でStreamにした後にごにょごにょStreamで操作した最後にto_list などでlistにして処理をする、といった例が載ってましたが、イマドキぽい感じがします 🙂More

[Elixir in Action]Erlang/Elixirの再帰計算におけるnon-tail recursionとtail recursion

ちょっと印象的だったので。 再帰計算を行う関数の最後が、別の関数呼び出しかどうか、という違いです。 non tail recursion https://github.com/sasa1977/elixir-in-action/tree/master/code_samples/ch03 tail recursion https://github.com/sasa1977/elixir-in-action/blob/master/code_samples/ch03/sum_list_tc.ex Dive to source code Elixirには、再帰表現をまとめたコードとして Enum.reduce(collection, acc, fun) なんかを提供しています。 この中身を少しみてみましょう。 Elixirの の再帰箇所は以下のように書かれています。 Erlangの資料によると、 :lists.foldl(Fun, Acc0, List) はtail recursionとのこと。 こちら foldl/3 is tail recursive and would usually be preferred to foldr/3. ということは、 Enum.reduce(collection, acc, fun) はtail recursionなのですね。 コード追うと Enum.reduce/2 も Enum.reduce/3 を結局は呼ぶので、reduceはtail recursionで実装されていて、巨大なリストに対してもちゃんと再帰計算ができることを重視されているのですね。 Elixirの List.foldr/3 や List.foldl/3…More

[Elixir][Phoenix]簡単なWebアプリを生成してみた

やったこと備忘録。 最近、細々とElixir 1.0.5 x Phoenix 0.15.0ベースで、簡単な投票Webアプリケーションを作成してみました。 時間にして、作成自体、1日はいかない感じ。EctoやPhoenixを調べながらやって、1日といったところでしょうか。 使ってみた感想としては、PhoenxiはまんまRailsに近しいな、ということですね。 Railsに明るくなくとも、このくらいのアプリだとある程度書き進めることもできます。 ちなみに、私は明るくない側の人間です。 実装したことはこちら リポジトリはこちら Phoenixの他に、ログインなどの状態を保ちたいので、Guardianという認証ライブラリを使いました。 これ書いてて感じたのですが、文法的な表現は確かにElixirのように書くのですが、Supervisorなんかは別に書くわけではないし、なんかElixirを書いている!という感じがしなくもない… 最近Elixir in Actionを読んでいるのですが、ErlangからみたElixirという側面の強い情報を仕入れているのですが、なかなか面白いですね。学び始めて少しして、急にQiitaなんかでもやってみた系記事を見始めたのですが、それよりも前の古参の方々や学び始めた人、ここら辺を最初の一歩としてインプットしてるのかな。 Elixirのテスト関連を中心に色々知見を深めたい。More

[Elixir in Action]Read building blocks

Elixir in Action、Programming ElixirやGetting Startedなんかとは趣が異なって、BEAM VM上で動作するElixirという仕組みを色々深堀しているのですね。説明していること自体はかぶる箇所が多々あるのですが、より言語自体というよりも、その言語によって何を成すか?という目的に対してこの機能がある、といった感じで話が進んでいる感じ。 ところどころ、Erlang混じりの解説やリンクが登場するのはErlangを専門とする著者だから、の特徴かな。これ読んでいると、Erlangの言語的な表記というよりは、内部処理の知見もたまってくるという。 少し読んだところの特徴を抜粋。個人的なメモです。 Elixirは、moduleとfunctionに分けられる Elixirは純粋な関数型言語ではない。そのため、幾つか副作用がある関数が存在する。 データはimmutable。加工したデータは、異なるmemory locationに書き出される。ただし、shallow copyされたデータは共通の元となるデータをなるべく共有している。 booleanは true と false のatom nullabilityは無いので、 nil のatomを使う String typeは無い。binariesかlistで使う。 character listは、3-rd party製のErlangなんかが要求する時のみに使うことが良い。あとはbinary string使いましょう。 complex typeはlist、tuple、mapsだけ Range、Keyword list、HashDict、HashSetはデータシステムの元 keyword-listは値の小さな構造体で使う HashDictは、より大きなコレクションで性能を上げたい時に使おう HashSetはユニークな値の集合 & オペレータでlambdaを使おう Getting started では、 fn x -> function end を簡略化するために & を使ってましたね 粒度が結構違うメモなのですが、Elixirでの命名規則は以下の模様。 Elixirでの命名規則としての ?、! ? : true or false…More

[Elixir in Action]No technology is a silver bullet.

ここ最近、ざっとGetting startedやらexercism.io、PhoenixとElixirやその周辺ライブラリを触って、ちょっとしたライブラリも作って。Phoenix使って簡単なWebアプリ作って、というところをしました。 そんな折、Elixir in Actionを読み始めました。他のElixirの本も読もうと思うのですが、Actionシリーズなのでまずはこちらから読んでみようかな、と思いまして。 この書籍は言語自体の話から、Elixirのプラットフォームの話へと進んでいきます。多分、他の書籍も同じ。ただ、この書籍の著者はErlangを専門とする人のようで、RubyからElixirに寄った人たちとは異なった視点からの意見を望めます。良いですね。 Erlang Erlangの話から。Erlangはfault tolerance、Scalability、Distribution、Responsiveness、Live updateを重視した、高い可用性(high availability)を目指した言語です。それらを実現するための核が、concurrency。BEAM VM上に存在するプロセスとプロセス間通信を構築するなどでconcurrencyを核に添えた開発環境を構築できる、と。 WhatsAppsなんかはかなり有名ですね。私もErlangは少し触れたことがあります。ただ、言語の記述には慣れなかった… Elixir Elixirは、Erlangの記述を単純化したり、拡張することで描き易くしたものです。一言でいうとそんな感じなのですが、そこは開発者を取り込むために必要なものです。ClojurやRubyに影響を受けているので、そちら方面の人は馴染みやすそう。 No technology is a silver bullet. ErlangやElixirにも苦手なところがあります。この書籍では、その点も書いていて良かったです。 Speed: CやC++にはかないません。BEAM VMは、ハードウェア資源をめいいっぱい使うので、貧弱なハードウェアだと性能劣化が発生します。これは多くのプロセスを立ち上げ、処理を行っていくという特徴を持つから。なので、CPUの拘束が長い処理を行う場合、別の言語を選んだほうが良いでしょう。 Ecosystem: 単純に、まだ、エコシステムとしては小さい。 まだまだ先は長いですが、ちょっとづつ読んでいこう。 こういうfault toleranceなシステムや分散システムを前提としたシステムのテスト、品質(Quality)をどうやって評価するか、それをどうやって安定した開発に繋げることができるか?といったところ、テストエンジニアとして技術的な視点では非常に挑戦的で面白さを感じますね。 ただ、それらの先にユーザがいなければ自己満足な世界で終わるのでそこは忘れてはダメですね。More