MIX AND OTP vol 1

ElixirのGetting startedも、Mix and OTPの話に入りました。

http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html

$ mix new project_name

でつくられる各ファイルの説明などから始まり、実際にExUnitでテストを書くまでの説明が載っています。

Agent

Elixirはimmutable languageなので、状態を保持するにはProcessesかETS(Erlang Term Storage)を使う必要があります。ここは、Getting StartのところのProcessやTaskのところで説明があった、TaskとかAgentの話ですね。

ExUnit

setup が用意されているのだけれど、このsetupのcallbackが後の test へ渡されるらしい。なので、setupで最後に渡す引数を渡して、それを test で受け取らないといけないのですね。

defmodule KV.BucketTest do
  use ExUnit.Case, async: true

  setup do
    {:ok, bucket} = KV.Bucket.start_link
    {:ok, bucket: bucket}
  end

  test "stores values by key", %{bucket: bucket} do
    assert KV.Bucket.get(bucket, "milk") == nil

    KV.Bucket.put(bucket, "milk", 3)
    assert KV.Bucket.get(bucket, "milk") == 3
  end
end

setupやsetup_allがあれば、teardownに該当するものもあるのかな?と思ってみると、 on_exit があるのですね。

ExUnit.Callbacksの中に、以下のような定義がありました。

  @spec on_exit(term, (() -> term)) :: :ok
  def on_exit(ref  make_ref, callback) do
    case ExUnit.OnExitHandler.add(self, ref, callback) do
      :ok -> :ok
      :error ->
        raise ArgumentError, "on_exit/1 callback can only be invoked from the test process"
    end
  end

これは ExUnit.OnExitHandler をcaseで呼んで使っている模様。
また、このon_exit自体はaddされるだけなので、ExUnit.OnExitHandlerのもとがどこにあるかコードで追ってみると、最終的には ExUnit.Runnerrun の中にある loop に行き着いた。この処理開始のときに、 ExUnit.OnExitHandler も登録されて処理が進み、 on_exit でOnExithandlerにfnを登録、テストケースが終わった時点で実行する、と。なるほど。

もう少し、ExUnitを遡ると、ExUnit のモジュールでstart/1が定義されていて、その中の System.at_exit にテストケースが登録されていることが一番の始まりみたい。

ちなみに、この at_exit の説明は以下。

Registers a program exit handler function.
Registers a function that will be invoked at the end of program execution. Useful for invoking a hook in “script” mode.
The handler always executes in a different process from the one it was registered in. As a consequence, any resources managed by the calling process (ETS tables, open files, etc.) won’t be available by the time the handler function is invoked.
The function must receive the exit status code as an argument.

なるほど。

ちなみに、ExUnitは以下のように行単位でテストを実施することも可能とか。

$ mix test test/kv_test.exs:4
Including tags: [line: "4"]
Excluding tags: [:test]

.

Finished in 0.07 seconds (0.07s on load, 0.00s on tests)
1 tests, 0 failures

GenServer

Elixirにbuild-inされているサーバ機能の説明です。processに状態を持てるという話は過去に出てきましたが、その状態の保持や監視などをElixirの1つの機能としてまとめたものがこれのようです。

以下の、受け取るcall_backの性質の説明箇所は大事そう。

http://elixir-lang.org/getting-started/mix-otp/genserver.html#call%2C-cast-or-info%3F

call,caseのそれぞれを選ぶ理由は以下。

Since any message, including the ones sent via send/2, go to handle_info/2, there is a chance unexpected messages will arrive to the server. Therefore, if we don’t define the catch-all clause, those messages could lead our supervisor to crash, because no clause would match.
We don’t need to worry about this for handle_call/3 and handle_cast/2 because these requests are only done via the GenServer API, so an unknown message is quite likely to be due to a developer mistake.

GenEvent

この章の最後らへん、 state というのがおもむろに出てきて、様々なcallbacksの引数に使われているのですが、それがどこから来たものかモヤモヤしていました。API documentをみると、 init のcallbacksの応答として、 {:ok, state} とあるのですね。そして、この state が他のcallbacksの引数で使われていたと。

モヤモヤが解決。

Supervisor and Application

ElixirというかErlang?の安定性を保つための、fail fastの思想の基盤となるsupervisorの説明です。

When things fail, your first reaction may be: “let’s rescue those errors”. But, as we have learned in the Getting Started guide, in Elixir we don’t have the defensive programming habit of rescuing exceptions, as commonly seen in other languages. Instead, we say “fail fast” or “let it crash”. If there is a bug that leads our registry to crash, we have nothing to worry about because we are going to setup a supervisor that will start a fresh copy of the registry.

様々なエラーに対して防御的なプログラミングをするよりも、エラーが発生したら早くクラッシュさせてしまおう、クラッシュする前の新鮮なコピーを素早く提供して安定させよう、という考え方。その中心を担うのがsupervisorの模様。

この再起動するために様々なstrategyが存在するようです。今のところ、 one_for_onesimple_one_for_one が出てきましたが、他にもいくつかあり、その中で一番必要なstrategyを設定すると良いらしいです。
http://elixir-lang.org/docs/stable/elixir/#!Supervisor.html


ここまで MIX AND OTP の中間くらいまできました。Elixir、良さそうですが、Erlangの持つ並列処理や安定性を正しく手にいれるためには、それなりの考え方をなじませる必要がありそうです。特に、サーバとして安定した運用をする上では必須な考え方のように見えます。

でも、今の所読み進めていると私は好きな感じ。

チュートリアルは以下に蓄積中 🙂
https://github.com/KazuCocoa/elixir-tutorial

広告

MIX AND OTP vol 1」への1件のフィードバック

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中