[Elixir]PhoenixのSupervisorのstrategy

Phoenixって、1 request 1 process って言われますね。でも、そうは言ってもどうして?というところが気になります。 その中身をちらっと追ってみたのでメモ。 process死んだらそのまま、ということは、リクエストに対する処理の信頼性に関してないだろうと思ってひとまずSupervisorあるだろうな。さらには、Phoenix、macroでゴニョゴニョしてるので、動的にSupervisorに子をセットする必要があって、なら最終的には ‘simple_one_for_one’ かなーってのが予想でした。 Phoenix.Supervisor 大元。 https://github.com/phoenixframework/phoenix/blob/8d2a3eed27ef693bf5363cdf88fbeb91422d0362/lib/phoenix/supervisor.ex Phoenix.Transports.LongPoll.Supervisor ここを one_for_one でよでいるみたい。 https://github.com/phoenixframework/phoenix/blob/01955e1d955781b62c362df9a166ae273a8da64d/lib/phoenix/transports/long_poll_server.ex defmodule Phoenix.Transports.LongPoll.Server https://github.com/phoenixframework/phoenix/blob/01955e1d955781b62c362df9a166ae273a8da64d/lib/phoenix/transports/long_poll_server.ex 先ほどのSupervisorのchildrenで読んでいたところは同じファイル内に同様に定義されています。 endpointに対してリンクスタートしてますね。 ひとまず、なるほどなー。 simple_one_for_one はあたってた。More

『すごいErlang ゆかいに学ぼう』を読んだ ~Erlangの地獄の最下層まで進もう~

『すごいErlang ゆかいに学ぼう』を読みました。すごいE本、Elixir学んでいると必然的に?いきつく先ですね。C++学ぶためにCを学ぶ、みたいな。 Programming Elixir、Elixir in Actionを事前に読んでいたので、内容の大半は頭の整理のためにさらっと読んだくらいでした。その中で、最後付近のCommon Testとかは目新しいものでした。王道としては、Programming Elixir + すごいE本、でしょうか。 ※すごいE本、もとはこのURLのもの?: http://www.ymotongpoo.com/works/lyse-ja/ 学習曲線 Elixir/Erlangの学習度合いがどのくらいかな、と思って残してみました。ここまでくるのにざっと平均して 3ヶ月 x 1h/day くらいかなーという個人的な感想です。この間に写経やら、ちょっとしたライブラリ作ったり。このくらいになれば、PhoenixやEcto、Elixirのコードを読みながら動作を理解できるようになるかな。という感じです。 Railsの経験が高かったり、Ruby、はたまた関数型の経験が高い人だとPhoenixやElixirをもっと効率的に学べそう。私は純粋なプログラマーの方々よりプログラミング能力はないのでこのくらいな感じです。 以下、この本を読んでちょくちょく気になったものをメモがてら残していきます。 atomに関して atom 結びつけたデータを表現したり制限したりするために使う Tuple内で使う atomはアトム表で参照される 1 atom は 4byte(32bit OS), 8byte(64bit OS) 消費する ガーベジコレクトの対象外 システムが落ちるか、1,048,577個のatomが宣言されるまで蓄積される(optionで変更は可能) 動的に生成されるべきではない 開発者のツール 末尾関数(tail recursion) これは、tail recursionであることをこのまえのblogでもかいた => blog 末尾呼び出しの場合、最適化処理が実施されるため、メモリの消費が増えない。 これは TCO: Tail Call Optimizationとお晴れ、LOC: Last Call Optimizationの特殊な一例とのこと。LOCは、関数の末尾の式が他関数を呼び出すときに実施される、スタックフレームの保存を避けるという最適化。 エラーハンドリング系 12章の、ビヘイビアなどのエラー関係は、Elixirやるにせよ重要そうですね。GenServer になる…More

[Elixir]GenServer/Agent/Taskの起動元

ElixirにはGenServer、Agent、Taskといったモジュールが存在します。これらは状態の保持やプロセスの起動などに特化したもの、汎用的なものといった役割の区分があります。 ちょっと、それらがどのように起動(start/start_link)されているのかちょっと見てみようと思ったので、そのメモです。 GenServer コード(Elixir 1.0.5) do_startの中で、start_linkで、最終的に :gen.start を呼んでいる。 https://github.com/elixir-lang/elixir/blob/v1.0.5/lib/elixir/lib/gen_server.ex#L318 Task Taskは、 start_link すると、 Supevisorの :simple_one_for_one のstrategyで Task.Supervised を起動するようです。コード(Elixir 1.0.5) 少し追って Task.Supervised をみると、Erlangの :proc_lib を起動しているようですね。 (Elixir 1.0.5) proc_libってなんだろう?と思ってみると、Erlangの以下のモジュールのよう。 http://www.erlang.org/doc/man/proc_lib.html Functions for asynchronous and synchronous start of processes adhering to the OTP design principles. OTPデザインの原則に従った、同期/非同期のプロセス起動を担うらしいです。 http://shibu.jp/erlang/design_principles/sys_and_proc_lib.html Agent Agentは、 GenServer を使っているようです。(Elixir 1.0.5) ここから、Agent.Servereへ向かっているよう。(Elixir 1.0.5) なるほどー。send/receiveをElixir独自でごにょごにょしていると思っていたら、基本Erlang/OTPの資産を使っているのですね。More

[Elixir]雑にプロセスの並行処理を書いてみる

Elixirというか、BEAM VMのプロセスが並行に動いてることをさくっと確認するため、個々のプロセスに適当な時間sleepした後にメッセージを返すだけの簡単なプログラムを組んでみました。 テストコード付き。 以下をコピペして、 elixir copy_file.exs で実行するとテストが通ります。 これを個別に実行すると以下のような感じ。 以下は、{:ok, プロセスID, 各プロセスにメッセージを送った順, sleepした時間} です。 これを見ると、個々のプロセスがそれぞれ並行して動作していて、一定時間待ったらgroup_leaderへメッセージを返している、という処理がプロセス毎に並行して実施されていることが確認できます。 雑に並行処理を書いてみましたが、これ、少し遊ぶと個々のプロセスを参加者とみなした問題の早解きとかできそうですね。はたまた、個々のプロセスが独立してるとすれば、社会システムを模倣したちょっとしたシミュレーションとかできそう。(やる意味は置いておいて…) 雑に処理を並行させてみた、でした。More

[Elixir]EctoのQueryを少し読み解く ~ コードに前提条件が書かれるので読み解きやすいですね ~

Ectoは、Elixirが標準として提供するDBへの接続ライブラリです。macroを学んだので、少しmacroを読んでみよう、ということで読んでみました。 ここでは、すべてEcto v0.16.0を元にしています。 Ecto.Queryの補完を見ればわかるのですが、この下にfromなどのSQL構文を持っています。 これらを使い、例えば、 Sample.User というリポジトリに含まれる要素に対してSQLを発行したい場合に以下のようにSQLを発行することができます。 これらを読んでいこうとすると、まずは以下の fromに関するdefmacro に出会います。 defmacro from(expr, kw \\ []) do コード このコードを読んでいくと、このmacroの先で幾つかの条件に分けれられた from が定義されています。 defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @binds do コード defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @no_binds do コード defp from([{join, expr}|t], env, count_bind, quoted, binds) when join…More

[Elixir]macroのテストを書いてみる

macroのテストを書く macroのテストでは、通常は コンパイルされたモジュールの振る舞い コンパイルされた結果(AST)自体 の2つの側面をテスト対象と見てテストコードを書いていくことになります。 振る舞いに関しては、以下のような感じでExUnitを使います。こちらは、普通にmacroによって定義された関数を使って、期待するinput/outputを確認する、というものです。 コンパイル自体に対しては、以下のようにAST自体が正しくできているか?を確認することになります。他は↑とさほど変わりません。inputがASTの文字列、outputがassertionになります。 例えば、以下のような内容になります。 少し話が逸れますが、Agentなどのテストにおいて、Processにsendされた値を確認したい時があります。つまり、mailboxの中身を確認したい。そんな時は以下のように __mailbox__ を使うことができます。 いつMacroを使うか? より簡潔にコードを記述できる場合 コード生成が必要な場合 例えば、Phoenixのルーティングではコード生成によってGET/POST/PUT/PATCH/DELETEなどの match を定義しています。 このような、少量のコードを書くことで、大量の汎用的なコードを生成する、というような場合、それら全てを手で書くよりも正しく、効率的に実行コードを生成できます ただし、複雑になると理解が難しくなるので、metaprogramingで大事なことは、simpleであることです。 mix-inするとき、 import を使えるなら use は使わない use を使うと、 __using__ を読み込みます。そのため、mix-inとしてimportのように使うことができます。ただ、macroをmix-inのためだけに使うのは止めましょう。単に複雑さを増すので。 macroを使ったmix-in: NO importを使ったmix-in: OK 他、いろいろありましたが大事なところはここら辺かな。 ここまでの内容は、『Metaprogramming Elixir』を読んだ内容が主でした。 __using__ の使い所とか、テストフレームワークとか作るのに必要なメタプログラミングの知見を得られたかな、という感じで、良い学びでした。ついでに、ちょくちょくElixir関連のOSSに貢献できたので、それも良かったかな 🙂 関連 [Elixir]macroを書くときの影響範囲 [Elixir]Macroで簡単なテストフレームワークを作ってみるMore

[Elixir]Macroで簡単なテストフレームワークを作ってみる

テストフレームワークと書きながら、そこは余り多く書いていませんが… Elixirは、 Elixir is small because it doesn’t have ti include all common features. とのこと。なので、他に必要な拡張が欲しければ、自分で作ってねという言語らしい。基本は小さく、という立ち方ですね。 bind_quoted 通常、 quote で囲まれたところのうち、変数をそのまま使いたい場合は unquote します。ただ、都度 unquote するのは少し面倒だし、場合によっては意図しない動作になります。その問題を解決するために、 bind_quote があります。 例えば、以下のASTが bind_quote の有無でどう変わるのかを覗いてみます。 unquoteのみ bindされたとき bind_quoteのときは、最初に処理が関連付けられて、それを以降のquoteでaliasのようにして呼び出している、という処理をするようですね。unquoteの有無で意味が大きく異なる処理もあるので、これは良さそうです。 このbind_quoteが有効な時は、unquoteはその範囲内では無効に設定されます。 なお、このほかにも https://github.com/elixir-lang/elixir/blob/f7183440716d705bfaabdc54c216d87cebacd9a7/lib/elixir/src/elixir_exp.erl#L146 にある、 がquoteのときにしていできる、特別なoptionのようです。 テストフレームワーク 簡単なテストフレームワークを書きました。以下リポジトリに突っ込んでいます。 内容自体は簡単で、 assert の実装 test で始まるメソッドをテストケースとして認識、実施する というものです。 https://github.com/KazuCocoa/my_mini_ex_test_assertion/tree/master サンプルを見ながらの実装ですが、これはそんなに難しくないです。 Compile-Time code generation コンパイル時にコードを生成、メソッドなんかで定義されるmacroの話です。 unquoteが、quoteの外で使われている場合があります。 これにより、以下のように動的な関数を定義することができる。 ここで、Elixirは、外部ファイルに依存している箇所を明示することができる。 これにより、Elixirは mimes_path…More

[Elixir]macroを書くときの影響範囲

ElixirのASTって、Lipsの文法をTupleで表現しているようなものなのですね。defmacro、quoteをMacro.expand_onceなんかで分解してみると、 {atom, [List], [List]}の形式で分解されていき、Lipsのような読み方で読み解くことができる、と。 なので、Elixirは Elixirの文法そのままでHigh Levelな表現で処理を記述する LipsのようなLow Levelな表現で処理系を記述する という2層で処理を記述することができる、と。High/Lowは、処理の抽象度を指しています。 後者がMacroになるわけですが、そんなMacroも下手に適用範囲を広げないためにcontextによる適用範囲を限定する機能を持っています。 Elixir Hygiene 以下の通り、 __MODULE__ により、contextが異なることがわかります。 quote で囲まれるところは、つまるところ、呼び出された側のcontextに限定された環境下でのみ、有効になる、ということを意味する。 Elixirはこのようにcontextを設定することで、macroの影響範囲を限定する手段をとっています。 Violate hygiene!! 通常、 quote で囲まれた範囲は、quoteで囲まれた中だけで有効です。 それ以外の領域には影響を及ぼすことはありません。 例えば、以下の通り name の変数は Setter.bind_name のマクロの範囲を出ることはありません。 しかし、以下のように var!/1 を使うとこのhygieneな領域を超えて変数をOverrideすることが可能になります。この機能は、本当に必要なときのみ使いましょう、と強く書かれてもいます。 他メモ Logger.debugは、productionのコンパイルが行われるときに削除される。 なるほど… Macroは処理系を書き換えてしまって、例えば Kernel.if でさえ異なる処理に書き換えることもできるので少し慎重になっていましたが、影響範囲を限定的にできる、その領域を超えるときは明示が必要、という姿勢が良いですね。 ElixirのMacroのドキュメントに以下が書かれていることからも、やっぱり明示に寄せている方に寄せておきたいですね。 Remember that explicit is better than implicit. Clear code is better than concise code. 追記…More

Ecto0.16.0でvalidate_uniqueがdeprecateになって、unique_constraintが追加された

これは、 Changeset の構造体に constraint の要素を設定し、DBへの処理を行うタイミングでその束縛に反していないかを確認する、というもののようです。 変更のあったコミット https://github.com/elixir-lang/ecto/commit/40c7edb69de8a30145e4e4d54cc8108cb1999630 これをざーっと眺めると、 lib/ecto/changeset.ex の moduledoc が更新されていることがわかります。 constraints と validation の関係が記載されているので、気になるかたは眺めてみてください。 However, constraints can only be checked in a safe way when performing the operation in the database. As consequence, validations are always checked before constraints. Constraints won’t even be checked in case validations failed. とのことです。 changeset.ex の defstruct に以下が追加されています。…More

[Elixir in Action]Elixirで分散システムを構成するための諸要素を知る

ここら辺がこの書籍の真骨頂でしょうか。 Chapter11ではよく知られたOTP applicationを作るためのファイル構成とか、書き方な話なのですっ飛ばして、Chapter12のメモ。 ざっと読んで、触った感覚としては、分散システムは無知では手を出さないほうが良いかなーということ。色々考慮漏れで溢れそうな予感… Erlangベースのシステムでは、processとmessageによって分散システムを構成します。(伝統的なRPCと混同しないように) BEAMによる分散システムは、複数のnodeがそれぞれ接続されてクラスタ化されることで実現されます。このnodeは、BEAMインスタンスと呼ばれます。 Nodeの接続 以下のようにして、簡単なnodeを立てて接続、クラスタを組むことができます。 node1 node2 node3 node1とnode2の後に以下を実施。 Clusterをすでに構築しているnodeに接続すると、自動的にそのCluster内の他nodeとの接続も行われます。これは、tick messageと呼ばれるメッセージのやり取りが行われるためです。これにより、Clusterに含まれるnodeの生存確認も行います。すでにdisconnectな状態のnodeがあれば、それは Node.list から除かれます。 Standard I/Oの実行と出力先 以下の通り、node1で実行した内容を、 Node.spawn でnode2に渡すと、その処理はnode2で行われ、結果をnode1で表示する、ということができます。 これは、すべてのstandard I/Oの出力はClusterのgroup leaderに渡されるためです。group leaderは、処理をinputされたnodeで、ここではnode1を指します。 ここで送るmessageには特に制限はないとのこと。このmessageは、 :erlang.termi_to_binary/1 でエンコードして送られて、受け取ったnodeは :erlang.binary_to_term/1 でデコードするそうです。 Cluster構成の前に nodeが互いにやり取りを行うにあたり、必ず以下の操作が必要になります。(送信元をclient、送信先をserverと表現) ClientがServerのPIDを取得する ClientがServerにmessageを送る そのため、まずはPIDを取得する必要があります。 PIDのlookupは基本的にlocalで行われます。そのため、目的のprocessを自身のlocalで見つけること自体は高速に行われます。 PIDにはルールがあって、以下のようになっています。 どのnode上にあるprocessか(localなら、ゼロ) local内でユニークなnodeの番号 ↑のnodeの番号が表現可能な範囲を超えると増加する Global関数を使って広範囲でClusterを構成する Global関数を使って、globalな領域でclusterを組むことができます。このglobalは、Erlangですでに用意されている関数です。 Elixirだと、GenServerなんかで start_link するときとか、 :global 指定で起動することができます。 https://github.com/elixir-lang/elixir/blob/e74852ffcc95872915be2b1aae453e74d6c54325/lib/elixir/lib/gen_server.ex#L132 実際にglobalを使うと、以下のような形でregisterとwhereisでPIDを得ることができます。 以下のように、なんらかの他nodeとリンクを張っていると以下のような情報が取得できます。 pg2関数を使ってグループに分ける :pg2関数を使ってグループを作ることもできます。このpg2関数では、同じエイリアス( :doro_list というところ)に複数のnodeをぶら下げることにより、1つのエイリアスに対して複数のnodeをグループ化できます。 ここでは、node1で元となるprocessを作り、そこにnode2とnode1が順に参加、グループとなる例を示しています。…More