[Elixir]Plugを使ってたときにPhoenixの1request-1processが気になったので追ってみた

ちょっとしたHTTPの処理を受けるサーバを Plug を使って実装してようとしたら、 Plug.Adapters.Cowboy.child_spec() をすることになりました。また、trotも少し処理を追ってみると同様なところに行き着く。 ふと作業している途中で、Phoenixだと、1 request – 1 processとなるendpointの処理は結局はどこが処理してそうなっているのだろう、という疑問がわいたので、 すべきことを横に置いて さかのぼってみました。 過去、似たようなところとして以下のようなものを追っていました。 [Elixir]Phoenix.Router付近やendpointを追ってみる [Elixir]PhoenixのSupervisorのstrategy 開始地点 ひとまず、雑に Plug.Adapters.Cowboy で検索してみました。これは、Elixirの機構を使っているなら、最終的に同様にPlugにいくだろう、とのことから。 すると、Plug.Adapters.Cowboy.Handler なんかが検索に引っかかります。 この中から、 Phoenix.Endpoint.CowboyHandler の child_spec の中で使われているのを見つけました。この child_spec は、Subversion Treeの中で呼ばれます。 同様に、このHandlerをみてみると、この行にて、 が呼ばれていることがわかります。つまり、Phoenixのendpointの処理は、最終的にはPlugを使い、Cowboyへと向かっているのですね。 なるほど。 Subversion Treeを遡る もう少し、今度はSubversionがどのように流れているのかを追ってみました。つまり、 Phoenix.Endpoint.CowboyHandler がSubversion Treeの中で呼ばれるところを探す、です。 検索してみると、Phoenix.Endpoint.Serverで、 として定義され、この@handler が同じファイル内の initの箇所で呼ばれています。 じゃあ、このPhoenix.Endpoint.ServerをどのSubervionが呼んでいるのか?というと、Phoenix.Endpoint.Adapterで呼ばれていました。 もう少しさかのぼると、 Phoenix.Endpoint.Adapter は Phoenix.Endpoint の def server()内で stat_linkされるようです。この server() は、同じファイルの __using__ で呼ばれているので、Phoenix.Endpoint…More

[Elixir]defmacroに2つの要素を持つTupleを与えるとき、例外的にTupleがそのままASTとして使われる

ex_parameterizedを修正していたときに遭遇した振る舞い。 以下の通り、簡単な defmacro でマクロを定義していたら、 Example.a(do: {1, 2}) のように、この場合だけ意図しない出力が得られました。 これ、 Example.a(do: {1, 2}) の出力として [do: {:{}, [line: 11], [1, 2]}] が得られないといけない、と思って 聞いてみた ら、これは以下の通り問題ないみたいですね。 http://elixir-lang.org/docs/master/elixir/Kernel.SpecialForms.html#quote/2 Quote literals Besides the tuple described above, Elixir has a few literals that when quoted return themselves. They are: つまるところ、この defmacro の中身の Example.a(do: {1, 2}) は、 {key, value} のtupleに該当するから [do: {1, 2}]…More

[Elixir]Parameterized Test with macros

hex.pmになかったのと、macroの練習がてら parameterized test の記述をサポートするmacroを書いてみました。 すごく簡単なminimamなmacroです 🙂 https://hex.pm/packages/ex_parameterized https://github.com/KazuCocoa/ex_parameterized こんな感じで test_with_params を接頭辞として使います。 内部では、 test をそのまま使っているのと、 line numberをそれぞれのテストケースで使っているので、テスト失敗した時でも何行目のパラメータで失敗したのかわかるようになってます。 Javaだと、@RunWith(Parameterized.class) や @RunWith(Theories.class) を使って書くやり方が推奨されているやつですね。(過去のBlog) RubyだとRSpecのモジュールやminitestとかでも実装されていますね。More

[Elixir]Elixirのライブラリ眺めてたらErlangのhttpcにたどり着いた

今はやりのElectronに触発されて、というわけでもないですが、ElixirでTwitterのStreamを取得できたりするライブラリである、ExTwitterの処理の一部を追ってみました。 その中で、 https://github.com/parroty/extwitter/blob/8820af239217a0f17864fd444fc71e451d5f5d79/lib/extwitter/api/streaming.ex#L92 ここの、以下のような process_stream の関数に含まれる receive のパターンマッチがイマイチ理解できませんでした。というのも、このライブラリの中に send self, {:http, …} というような応答を生成するところがなく、どこからこの receive にメッセージが来ているのだろう、とよくわからなかったからです。 結果的に、Erlangの httpc モジュールから来ているのだとわかったのですが、その中で最終的にErlangモジュールまで手を出すようになったので、私の後学の意味も含めてメモを残しておきます。 process_stream の場所 まずは入り口からです。Streamを取得する stream_filter の処理を追うと、以下メソッドに行き着きます。 https://github.com/parroty/extwitter/blob/8820af239217a0f17864fd444fc71e451d5f5d79/lib/extwitter/api/streaming.ex#L19 この分岐を持つ関数 process_stream は以下箇所で初めて呼ばれます。 https://github.com/parroty/extwitter/blob/8820af239217a0f17864fd444fc71e451d5f5d79/lib/extwitter/api/streaming.ex#L48 spawnとして別プロセスにメッセージを送っている箇所を知る ここをみると、spawn としてプロセスにメッセージを送っている処理の中で、 ExTwitter.OAuth.request_async をよんでます。 これを少し追うと、以下に行き着きます。 https://github.com/parroty/extwitter/blob/8820af239217a0f17864fd444fc71e451d5f5d79/lib/extwitter/oauth.ex#L23 erlang-oauth に足を踏み入れる このモジュール :oauth はErlangの erlang-oauth というモジュールだそうです。そこで、少しそのモジュールの get を追ってみます。引数6個なので、以下だとわかります。 https://github.com/tim/erlang-oauth/blob/master/src/oauth.erl#L24 ここでhttp_requestをみると、 httpc:request/4 を呼んでいることがわかります。 https://github.com/tim/erlang-oauth/blob/master/src/oauth.erl#L193 Erlangのhttpcドキュメントを追う ここで、Erlangのドキュメントをみてみました。 http://erlang.org/doc/man/httpc.html#request-4 また、 Options に関してはその内容が以下のように書かれていました。…More

[Elixir]SupervisorとWorkerの再起動の振る舞いをちょろっと見てみた

ちょろっとしたお遊び程度ですが、Supervisorによって管理されているプロセスをkillしたりしてプロセスをみてみました。書籍には載っているのですが、特に何も見ずにお遊び程度にサラでやってみたのでそのメモ、みたいな感じです。 初めに… 空のsupervisorプロジェクトを作ります。 $vim lib/supervisor_test/neko_supervisor.ex $ vim lib/supervisor_test.ex Supervisorの確認 Supervisor.count_children/1 を使い、親のSupervisorのしたに :neko の子workerがぶら下がっていることを確認してみます。 workerをexitしてみる Supervisorの one_for_one では、監視対象のプロセスが終了したら、異なるPIDを持つプロセスを起動する、というstrategyでした。Supervisorをexitしてみて、その挙動を実際に確認してみます。 Supervisorにぶら下がる子workerをみる ここで、親のSupervisorのPIDを元に、そのしたにぶら下がっているプロセスをみてみます。 1回exitした one_for_one の子プロセスである :neko 、exitしたら再度立ち上げられていますね。ちゃんと、PIDが異なる値になって、別プロセスが立ち上がっていることも確認できました。なるほど。 子プロセスを終了させる、という意味だと、Supervisor.terminate_child/2 もあるのですが、こちらはSupervisorから終了させるので、停止させたら復活しませんでした。その後、Supervisor.restart_child/2 で再び動き始めます。なるほど。 One More Thing Supervisorをkillしてみます。すると、Supervisorの子も全て終了していることがわかります。まぁ、そうですよね。 このようにSupervisorが落ちるとその子も落ちるので、Supervisorを階層でつなげて、それぞれに対しても様々なstrategyを設定できるようにすることが大事ですね。そこらへんの設計が大事で難しい、という話もElixir in Actionで触れられてたなー。 分散環境はやっぱり大局的な設計はすごく大事。More

[Elixir]Phoenix.Router付近やendpointを追ってみる

Phoenixのrouterにおける、macroによる動的な関数定義を追ってみました。その備忘録。 macroを多分に使っているといわれるPhoenixの入り口を覗いて仕組みを少し知ることが目的です。きっと、ここら辺追えるとテストフレームワークとかそこらへんも追ったり作れるだろうと踏んで。 まず、入り口のおさらいから。 macroによる定義のおさらい Sample Caller の2種類のモジュールを定義します。そのうち、 Sample では様々な方法で関数を定義します。Caller では、その Sample を use して、定義されたマクロの1つである my_def を使い関数を定義、実施します。こうなると、例えば my_def が get などで置き換わった時、その引数となった文字列を関数のように扱いそのブロックを実行する、ということも可能になります。 (最後には実際のPhoneixを使い、追ってみます。) SampleとCallerのモジュール定義 サンプルコードを以下に貼り。注釈でそれぞれのメモを追加してます。 これを sample.exs と保存して、以下のCLIを実行します。 Sampleの実行それぞれ 補完される内容 neko() は、quoteで囲まれたところはASTとして得られます。 inu() は、quoteのなかで、unquoteしてneko()を呼んでいます。 pig_a などは、 def unquote(String.to_atom(@value “_” num))() do と :atom として定義、unquoteされた要素は動的に関数として定義されます。 Callerの実行 補完される内容 my_def で定義した my_neko は、 @my_sample に要素として登録され、compile timeで MODULE の関数として登録されたものです。 __using__ で定義されたrunを実行…More

[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