Why am I getting an `unknown registry` error? How can I start and then find a Registry in my application supervision tree?

In my lib/myapp/application.ex file I set up a Registry and I give it the name :myapp_registry:

  def start(_type, _args) do
    children = [
      # Start the Ecto repository
      MyApp.Repo,
      # Start the Telemetry supervisor
      MyAppWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: MyApp.PubSub},
      # Start the Endpoint (http/https)
      MyAppWeb.Endpoint,
      {Registry, [keys: :unique, name: :myapp_registry]},
      # , Start a worker by calling: MyApp.Worker.start_link(arg)
      # {MyApp.Worker, arg}
    ]

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

Then, in another module, when some event happens, I try and see what keys are in my registry:

IO.inspect(Registry.keys(:myapp_registry, self()))

But I get an error:

(ArgumentError) unknown registry: :myapp_registry

So… how do I start a Registry in my Phoenix application supervision tree and give it a name such that I can find and use my Registry later?

When and where is IO.inspect(Registry.keys(:myapp_registry, self())) called? Could it be called during compilation or before the registry is started, like in endpoint’s init callback?

Thanks for responding @ruslandoga

I have it working now – I think what happened is that I did not restart mix phx.server after adjusting application.ex.

I now have code that looks like this:

def start(_type, _args) do
    children = [
      {Registry, [keys: :unique, name: MyAppProcessRegistry]},
      # Start the Ecto repository
      MyApp.Repo,
      # Start the Telemetry supervisor
      MyAppWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: MyApp.PubSub},
      # Start the Endpoint (http/https)
      MyAppWeb.Endpoint,
      # , Start a worker by calling: MyApp.Worker.start_link(arg)
      # {MyApp.Worker, arg}
    ]

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

and from my other module, at a later point, I am able to find and use the registry:

pid = spawn(fn -> special_work(id_one, id_two, id_three) end)
Registry.register(MyAppProcessRegistry, "#{id_one}_#{id_two}_#{id_three}", pid)
IO.inspect(Registry.lookup(MyAppProcessRegistry, "#{id_one}_#{id_two}_#{id_three}"))
# prints [{#PID<0.875.0>, #PID<0.879.0>}]

But I’m still having another problem – when I later want to lookup this pid from an entirely different process, it doesn’t work.

# in a completely different process, at some indefinite time later
IO.inspect(Registry.lookup(MyAppProcessRegistry, "#{id_one}_#{id_two}_#{id_three}"))
#prints []

Also, I can’t figure out how to just print out all the keys in the Registry. The keys method requires a pid to be passed in and it only returns keys registered by that pid. I just want to see the keys to all processes that any process may have registered.

# from some other process at a later time that only wants to lookup pid, never register them...
Registry.keys(MyAppProcessRegistry, some_pid) # <-- but I don't know the pid

Why do I have to pass in some_pid??? – shouldn’t I be getting values based on keys, not getting keys based on values?

Shouldn’t Registry.keys only take a single argument – the registry name. I just want a list of all the keys in the registry. Why does it take two arguments?

You can use select: Registry — Elixir v1.12.3

Ok, it seems like the link to those docs for select is good progress for me. But my progress nevertheless remains extraordinarily slow, and I am really puzzled…

# when one event handler is called I do this:
Registry.register(MyAppProcessRegistry, "hello", :world)
IO.inspect(Registry.lookup(MyAppProcessRegistry, "hello"))
# prints [{#PID<0.849.0>, :world}]

# I then immediately do this:
Task.async(fn ->
    IO.inspect(Registry.lookup(MyAppProcessRegistry, "hello"))
end) |> Task.await()
# prints [{#PID<0.849.0>, :world}]

# so far so good...

# Then the event handler in another process is invoked further down in my module, and I do this:
IO.inspect(Registry.lookup(MyAppProcessRegistry, "hello"))
# prints []

How can this be?

I verified that the value I stored was there and I verified that I could see it from another process through Task.async – but as soon as the other process is my event handler where I actually want it to run, it suddenly doesn’t work anymore.

Also, in the event handler where I actually want to do the lookup, this code shows an empty list:

IO.inspect(Registry.select(MyAppProcessRegistry, [
        {{:"$1", :"$2", :"$3"}, [], [{{:"$1", :"$2", :"$3"}}]}
      ])
)

I copied it directly from the example in the docs which indicate that I should “get everything from the registry”

So it seems that I’m really not using the same process registry, somehow… but I’m using the same name, of course, and all I do is start it in my application.ex file, as described in my post above.

iex(1)> :MyAppProcessRegistry == MyAppProcessRegistry
false
iex(2)> Elixir.MyAppProcessRegistry == MyAppProcessRegistry
true

Sorry, that was a typo on my part – I’m not using a :MyAppProcessRegistry

I have the name as MyAppProcessRegistry throughout

As soon as I copy-paste this code into the original event-handler where I register the key-value, or into the Task.async that I also call from there, it prints out the expected stuff from the registry.

But as soon as I copy-paste this code into the event-handler for the event where I actually want to do something, suddenly this code indicates that the Registry is empty.

IO.inspect(Registry.select(MyAppProcessRegistry, [
        {{:"$1", :"$2", :"$3"}, [], [{{:"$1", :"$2", :"$3"}}]}
      ])
)

# prints [] from the important event-handler

# prints [{"hello", #PID<0.848.0>, :world}] from Task.async or just after I register the key-value

Could it be that the process exits before your call Registry.select in your event handler?

2 Likes

YES

I feel like I owe you a thousand dollars. Thanks so much.

I guess the thing which was not clear to me from reading the docs is that key-values in the Registry are tied to a living process.

I had thought that the way it worked was that you spawned processes, got their pids, and then registered the pids with a key which you could then later use to retrieve the pids.

But in fact it appears that a “process registry” is not a place for registering processes, but a place for processes to register whatever values they want under arbitrary keys and all their data gets wiped out when the process dies.

Might be relevant:

2 Likes