Elixir1.0.5 は Erlang18 に未対応ではなかった
というのを初めて知った。
きっかけは、dialyxir を使おうと思ったから。
dialyxir をインストールする時の必ずエラーが発生しインストールに失敗する。 エラーの内容は以下の通り。
Creating PLT /home/ymmtmsys/.dialyxir_core_18_1.0.5.plt ... ** (FunctionClauseError) no function clause matching in Keyword.get/3 (elixir) lib/keyword.ex:118: Keyword.get(:no_such_file, :files, nil) lib/mix/tasks/dialyzer.plt.ex:132: Mix.Tasks.Dialyzer.Plt.core_plt_contains?/2 lib/mix/tasks/dialyzer.plt.ex:123: anonymous fn/1 in Mix.Tasks.Dialyzer.Plt.missing_apps/0 (elixir) lib/enum.ex:662: anonymous fn/3 in Enum.filter/2 (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3 (elixir) lib/enum.ex:662: Enum.filter/2 lib/mix/tasks/dialyzer.plt.ex:106: Mix.Tasks.Dialyzer.Plt.need_add?/0 lib/mix/tasks/dialyzer.plt.ex:57: Mix.Tasks.Dialyzer.Plt.run/1
:no_such_file
ということなのでファイルが存在していなくエラーになっているっぽいが。。。よくわからないのでググってみると
こんなissueを発見。そしてコメントに
と書かれているのを発見。
...あれ、今まで動いていたから気付いていなかったけど、まだ Erlang18 は対応してないっぽい。
Elixir の github を覗いてみると、確かにv1.2.0でErlan1 18.0に対応するようだ
とりあえず、自分の環境の Erlang のバージョンを下げよう。
追記 (2015/09/15)
インストールに失敗する原因は、Erlang のバージョンではなくメモリ不足によるものだった。 512Mのメモリを積んだVM上でインストールして失敗を繰り返していたたけれど、2Gに増やしたら成功した。 ちなみに1Gでは失敗した。
自分のPCは4GBしか積んでないので、VMに2Gも割けないのでインストールが終わったら通常の512Mに戻した。
追記 (2015/09/29)
https://github.com/elixir-lang/elixir/releases/tag/v1.0.5 を見るとErlang18.0 に対応したと書いてありました。
HTTPoisonのプーリング
HTTPクライアントにHTTPoisonを利用してみた。
同時実行数を増やすと50コネクション以上のコネクションは作成されないことがわかった。コードを見てみると、内部ではhackneyが使われていることがわかった。
hackney のドキュメントをみると、何も指定がなければデフォルトのプールが利用されるようだ(これ?)。
変更するためにはどうしたらよいかを調べた。
プールの数を変更する方法
名前をつけてプールを開始する。
poolname = :mypool options = [max_connections: 10] :hackney_pool.start_pool(poolname, options)
HTTPリクエストを送る際のパラメータで、[hackney: [pool: :mypool]]
を渡す。
HTTPoison.get!("http://localhost:4001", %{}, hackney: [pool: :mypool])
これでデフォルトではなく、自分の希望通りのプールサイズになる。
調べた時のライブラリのバージョン
- httpoison, 0.7.2
- hackney, 1.3.1
Phoenix で Poolboy を使ってコネクション(プロセス)プーリング
Poolboy を使ってコネクションプーリングができるようにする。接続先は memcached。
サンプルのhello_phoenixを元にした。手を加えたファイルだけ以下に貼っておく (ちなみに Phoenix は v1.0.0)。
実行してみと、、、
% curl localhost:4000 {"val":"Hello Phoenix"}
成功。
メモ
- mcd はプロセスを生成した時点ではmemcached と接続されていない。ので、start_link してすぐに使おうとすると、結果が取れない。
max_overflow
の値を0以上にすると、プールしている数がsize
に達していてもさらにそこからmax_overflow
分だけプロセスを生成する。max_overflow
分のプロセスは必要になった時点で生成されるので、01 で書いたとおり、コネクションが確立されていないプロセスが渡される時がある(というか、全てそうだった)。
感想
Phoenixのデプロイ
Erlang (Elixir) の機能の中で気になっているのが、ホットデプロイだ。といっても、そういう機能があるからといって気軽にデプロイできるものでもないらしいけど。
デプロイに関するスライド。
- http://sile.github.io/slide/kbkz_tech_05/#/
- http://www.slideshare.net/ohr486/shibuyaex-1-elixir?next_slideshow=1
デプロイ方法
Exrm
Exrm (Elixir Release Manager) を使うのがよさげ。 Phoenix 公式でも紹介されている。 設定方法などはそちらを参照。
パッケージ作成
MIX_ENV=prod mix release
で本番環境用のリリースパッケージが rel/hello_phoenix/releases
の下にバージョンごとに作成される。
起動
$ ./rel/hello_phoenix/bin/hello_phoenix start
停止するときは stop で。
更新
mix.ex に書かれているバージョンを上げ、MIX_ENV=prod mix release
でパッケージを作成する。
(ここでは バージョンをv0.0.1 からv0.0.2に上るとする。)
v0.0.1のアプリが稼働中の状態でv0.0.2に更新するには
rel/hello_phoenix/bin/hello_phoenix upgrade 0.0.2
で、OK。
自分の環境で試した限りでは、更新時に接続できないということはなかった。
mix phoenix.new のオプション
アプリケーション作成時のオプションに何があるか調べてみた(といっても、大して調べてないけれど)。
答えはここに。
- --app
- アプリケーション名?よくわかってない。
- --module
- モジュール名を指定する。phoenix.new したときのディレクトリ名とモジュール名を異なる名前にできる。
- --database
- PostgreSQL以外のDBを使うときに。
- --no-brunch
- brunchを使わないときに指定する。APIサーバとして使うならこれを指定した方がいいかも。
- --no-ecto
- Ectoを使用しない場合に使用。
Phoenix のアップデート
v0.17.0 がリリースされていたので更新してみる。フレームワーク(というか、Mixでインストールしたライブラリ)をアップデートするのは今回が初めて。
% mix archive.install https://github.com/phoenixframework/phoenix/releases/download/v0.17.0/phoenix_new-0.17.0.ez Found existing archive(s): phoenix_new-0.16.1.ez. Are you sure you want to replace them? [Yn] y * creating .mix/archives/phoenix_new-0.17.0.ez
特別悩むことはなくアップデート完了。インストールと同じコマンドで大丈夫だった。
変更点は http://www.phoenixframework.org/blog/upgrading-from-v016-to-v017 に書かれている。
大きな変更はないけれど、個人的に良かったと思う点は、パラメータに %{"format" => "html"} が追加されなくなったところ。jsonしか返さないAPIサーバを作る場合とか邪魔だったので。
求めていたのはinspect/2だった
to_string/1 ではなくinspect/2 だった。
iex(1)> to_string %{a: 1, b: "hoyhoy"} ** (Protocol.UndefinedError) protocol String.Chars not implemented for %{a: 1, b: "hoyhoy"} (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1 (elixir) lib/string/chars.ex:17: String.Chars.to_string/1
iex(1)> inspect %{a: 1, b: "hoyhoy"} "%{a: 1, b: \"hoyhoy\"}"
Map には to_string が定義されていないので、どうすればログに出力できるのかを悩んでいたのだけれど、これで解決。