10

Working through the Supervisor and Application part of the Getting Started tutorial, I'm writing a unit test for the supervision tree problem at the bottom. I tried starting the top-level supervisor but it failed with this error:

  1) test all buckets die if registry dies (KV.SupervisorTest)
     test/kv/supervisor_test.exs:4
     ** (EXIT from #PID<0.111.0>) shutdown: failed to start child: GenEvent
         ** (EXIT) already started: #PID<0.75.0>

Apparently the app is already started, so I need to get access to its worker processes. I could use Supervisor.which_children to get them if I had the supervisor. To get that, maybe it would help to have the running kv application:

iex(28)> kvpid = :application.info[:running][:kv]
#PID<0.77.0>

So now I have the PID for the app. Is there some way to get the root supervisor process from this, or do I have to manually register it somewhere to get at it from the test?

Or is there a way to directly get the workers from their names? I tried :erlang.whereis but it doesn't find the worker:

iex(33)> :erlang.whereis KV.Registry
:undefined

I tried using the name of the module directly, but that doesn't work either:

test "all buckets die if registry dies" do
    reg = KV.Registry
    KV.Registry.create(reg, "shopping")
    {:ok, shopping_bucket} = KV.Registry.lookup(reg, "shopping")

    Process.exit(reg, :shutdown)
    assert_receive {:exit, "shopping", ^shopping_bucket}
end

It fails with this error:

1) test all buckets die if registry dies (KV.SupervisorTest)
   test/kv/supervisor_test.exs:4
   ** (ArgumentError) argument error
   stacktrace:
     :erlang.send(KV.Registry, {:"$gen_cast", {:create, "shopping"}})
     (elixir) lib/gen_server.ex:424: GenServer.do_send/2
     test/kv/supervisor_test.exs:6

The code is up on github.

ijt
  • 2,259
  • 20
  • 28
  • To get the branch for this question, you have to add this step: `$ git checkout -b s_o_question origin/s_o_find_worker_question` – ijt Mar 04 '15 at 06:05

3 Answers3

10

You can't find KV.Registry because there's a typo in your code. You call:

worker(KV.Registry, [@manager_name, [name: @registry_name]])

but the definition is:

def start_link(event_manager, buckets_supervisor, opts \\ []) do

So you pass [name: KV.Registry] as buckets_supervisor, opts is [] and your worker is therefore not registered under the name KV.Registry.

Try this patch: https://github.com/mprymek/kv/commit/03ce2e4e5ab4287db2fab6de0bb1aeaf0226346f

 iex(1)> :erlang.whereis KV.Registry
 #PID<0.111.0>
Miroslav Prymek
  • 1,633
  • 1
  • 9
  • 12
  • P.S. if your code correct functioning depends on some argument, it probably should be mandatory. If you defined you function as "def start_link(event_manager, buckets_supervisor, opts)", it would not happen. – Miroslav Prymek Mar 04 '15 at 08:22
  • Changing the worker line did it: `worker(KV.Registry, [@manager_name, @bucket_sup_name, [name: @registry_name]])` – ijt Mar 04 '15 at 18:34
6

If you start the supervisor given it a name, you can get all workers with:

Supervisor.which_children(MyApp.Supervisor)

What is likely happening is that you are trying to start two worker(GenEvent, ...) and they will have duplicated events. Passing the :id option explicitly will likely fix it.

José Valim
  • 47,390
  • 9
  • 122
  • 109
  • Here's what happens: `iex(1)> Supervisor.which_children(KV.Supervisor) ** (exit) exited in: GenServer.call(KV.Supervisor, :which_children, :infinity) ** (EXIT) no process (elixir) lib/gen_server.ex:356: GenServer.call/3` even though I can see that the KV.Supervisor has started from a log message. I only have one call to `worker(GenEvent, ...)`. – ijt Mar 04 '15 at 18:42
  • Ah, I see. I needed to change the KV.Supervisor.start_link body to `Supervisor.start_link(__MODULE__, :ok, [name: KV.Supervisor])`. Now your snippet works. – ijt Mar 04 '15 at 18:47
1

After cloning down your repo I looked around. I was unable to start the application. Normally Elixir applications can be started from the command line by running:

iex -S mix

But when I ran your application I got the following error:

** (Mix) Could not start application kv: exited in: KV.start(:normal, [])
** (EXIT) an exception was raised:
    ** (UndefinedFunctionError) undefined function: KV.Supervisor.start_link/0 (module KV.Supervisor is not available)
        KV.Supervisor.start_link()
        (kernel) application_master.erl:272: :application_master.start_it_old/4

This means that in lib/kv.ex on line 5 your application makes a call to a supervisor named KV.Supervisor. I looked at your code and no module has that name, but I see you have module use the supervisor behavior named KV.Bucket.Supervisor.

You will need to either define a module named KV.Supervisor that implements the supervisor behavior, or update line 5 in lib/kv.ex so it calls KV.Bucket.Supervisor.start_link instead of KV.Supervisor.start_link.

Once you do that you should be able to get all the processes the supervisor is supervising by calling this:

Supervisor.which_children(KV.Supervisor) # Pass in the name of your supervisor module

Hope this helps!

Stratus3D
  • 4,023
  • 2
  • 30
  • 58
  • KV.Supervisor is defined starting at line 42 here: https://github.com/ijt/kv/blob/s_o_find_worker_question/lib/kv/supervisor.ex. I'm not sure I understand. – ijt Mar 04 '15 at 00:33
  • There's one more step when you clone the repo. You also have to check out the branch: git checkout -b s_o_question origin/s_o_find_worker_question. – ijt Mar 04 '15 at 06:04