Help us understand the problem. What is going on with this article?

はじめてな Elixir(21) OTPのスーパバイザを使ってみる

OTP のスーパバイザを使ってみます。使ってるテキスト「プログラミングElixir」の17.1節がそのままでは動かないので、修正を試みます。テキストは Elixir 1.2 ベースで(1.2節による)、ここで動作させてる環境が Elixir 1.7.4 です。

なお、ここではテキストの内容を動かすための記載しかなくて、テキストの内容以上の新しい話はありません。

プログラミングElixir 17.1 の修正

17章が「OTP: スーパーバイザ」です。17.1節「スーパーバイザとワーカー」の210ページの先頭から異なってきます。以下、211ページまでを修正して動かしてみます。
投稿後に215ページについての修正を追加

mix new --sup sequence の結果

mix new --sup sequence の lib の下の出力はテキストでは lib/sequence.ex になってますが、実際にはこの他に lib/sequence/application.ex が生成されます。

iex.ex
$ mix new --sup sequence
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/sequence.ex
* creating lib/sequence/application.ex
* creating test
* creating test/test_helper.exs
* creating test/sequence_test.exs

テキストで「だが、lib/sequence.ex を開いてみよう。」とある通りに lib/sequence.ex を見てもこんなのが出てきます。

sequence.ex
defmodule Sequence do
  @moduledoc """
  Documentation for Sequence.
  """

  @doc """
  Hello world.

  ## Examples

      iex> Sequence.hello()
      :world

  """
  def hello do
    :world
  end
end

ここは「だが、lib/sequence/application.ex を開いてみよう。」と思って見て下さい。

application.ex
defmodule Sequence.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Starts a worker by calling: Sequence.Worker.start_link(arg)
      # {Sequence.Worker, arg},
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Sequence.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Sequence サーバのコピー

テキストには「lib/sequenceディレクトリを作り、」とありますが、mix の結果ですでに生成されています。

その後に「前章で作った Sequence.Server モジュールをその中にコピーする。」とあるのは、「前章で作った Sequence.Server モジュールを定義している server.ex を lib/sequence にコピーする。」と解釈して下さい。なお、server.ex を lib の下にコピーするのでも動きます。

アプリケーションの定義

アプリケーションモジュールを定義します。なおここで言う「アプリケーション」はテキスト18章先頭にあるように Erlang 用語で言うところの「アプリケーション」です。

テキストでは「そして、childrenリストにある…」とあるところはlib/sequence.ex でなく lib/sequence/application.ex に対する言及です。こちらのファイルを修正して下さい。

ここでは worker 関数を定義しています。これはテキスト同様にテンプレートを修正して下さい。
さらに import Supervisor.Spec を入れて下さい。この import 文はテキストに寄るとテンプレートとして最初から入っていることになっていますが、なぜか入っておらず、手で入れないとなりません。

application.ex
defmodule Sequence.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false # この行を追加する
    children = [
      worker(Sequence.Server, [123])    # この行を修正する
    ]

    opts = [strategy: :one_for_one, name: Sequence.Supervisor]
    {:ok, _pid} = Supervisor.start_link(children, opts)
  end
end

実行

ここまでくるとあとはテキスト通りに試せます。

iex.ex
$ iex -S mix
Erlang/OTP 21 [erts-10.1.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Compiling 3 files (.ex)
Generated sequence app
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Sequence.Server.increment_number 3
:ok
iex(2)> Sequence.Server.next_number
126

とここまでは順調で…

iex.ex
iex(3)> Sequence.Server.increment_number "cat"
:ok
iex(4)> 
11:12:46.339 [error] GenServer Sequence.Server terminating
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+(127, "cat")
    (sequence) lib/sequence/server.ex:29: Sequence.Server.handle_cast/2
    (stdlib) gen_server.erl:637: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:711: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:increment_number, "cat"}}
State: [data: [{'State', "My current state is '127', and I'm happy"}]]
iex(4)> Sequence.Server.next_number           
123
iex(5)> Sequence.Server.next_number
124

と、強制的にプロセスを終了させるとスーパバイザがプロセスを再起動する様子が見て取れます。

stash サーバ付きの場合の修正

投稿後にテキスト見直してもう一箇所修正ありましたので追加します。215ページ一番下で sequence.ex を修正していますが、これは lib/sequence/application.ex です。

参考文献

プログラミングElixir
Elixir 1.7.3 Supervisor

謝辞

これは 2018.11.16(fri) に開催された fukuoka.ex#15:Elixir/Phoenixもくもく会~入門もあるよ(19:00開始) でもくもくした成果です。準備してくれたみなさま、ありがとうございました。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away