[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

[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