/var/log/messages

May 23, 2019 - 2 minute read - Comments - programming

プログラミング Elixir (32)

OTP:スーパーバイザーの章。バージョンが違うおかげで差分があるみたい。

とりあえず

プロジェクト作成。

* 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/application.ex になっている模様。

lib/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 が以下。

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 から server.ex をコピィ。で、children なナニを以下に。

    children = [
      # Starts a worker by calling: Sequence.Worker.start_link(arg)
      # {Sequence.Worker, arg}
      worker(Sequence.Server, [123])
    ]

以下な流れ、なのかな。

  • アプリケーションを開始すると Sequence.Application の start が呼び出され
  • children は子サーバのリスト、それぞれが何をするか、の仕様を作るために worker を使う
  • Supervisor.start_link に子サーバの仕様リストを渡し、これに基づいてスーパーバイザプロセスは子プロセスを生成
  • スーパーバイザプロセスは管理対象の子サーバについて start_link を呼び出す

試してみます。

$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
== Compilation error in file lib/sequence/application.ex ==
** (CompileError) lib/sequence/application.ex:13: undefined function worker/2
    (elixir) src/elixir_locals.erl:107: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
    (elixir) src/elixir_locals.erl:107: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6

ありゃ。色々確認してて以下を発見。

例示されているナニが以下。

children = [
  worker(SimpleQueue, [], name: SimpleQueue)
]

修正してみたら worker/3 なんて知らぬ、と言われてorz

対処

import が足りてなかった模様。こうして

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

コンパイルが通った模様。

$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
Generated sequence app
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

動作確認。

$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
Generated sequence app
Interactive Elixir (1.8.2) - 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(3)> Sequence.Server.increment_number "cat"
:ok
iex(4)>
21:06:24.726 [error] GenServer Sequence.Server terminating
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+(127, "cat")
    (sequence) lib/sequence/server.ex:21: 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"}]]
nil
iex(5)>

で、next_number してみます。

iex(5)> Sequence.Server.next_number           
123
iex(6)> Sequence.Server.next_number
124

プロセスは生き返り、サーバの状態はリセットされてます。

以降

エントリ改めて云々、ということで。

プログラミング Elixir (31) もくもく

comments powered by Disqus