-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
When registering a process in Registry using the :name
option of GenServer.start_link/3
, you can pass two kinds of tuples as the name:
- Tuple without metadata, e.g.
{:via, Registry, {RegistryName, "key"}}
. - Tuple with associated metadata:
{:via, Registry, {RegistryName, "key", %{meta: "data"}}}
.
Let's assume we registered a process with metadata; if you later send a message to the process via GenServer.call/2
using the via-tuple, both form will work:
GenServer.call({:via, Registry, {RegistryName, "key"}}, :msg)
GenServer.call({:via, Registry, {RegistryName, "key", %{meta: "data"}}}, :msg)
But it isn't the case for GenServer.cast/2
— when using the via-tuple form with metadata to refer to the process, it doesn't receive any messages while tuple name without metadata still works.
Current behavior
Consider this snippet:
#!/usr/bin/env elixir
ExUnit.start()
defmodule Counter do
use GenServer
def start_link(opts) do
initial_value = Keyword.get(opts, :initial_value, 0)
GenServer.start_link(__MODULE__, initial_value, opts)
end
def init(initial_value) do
{:ok, initial_value}
end
def value(counter) do
GenServer.call(counter, :value)
end
def inc(counter, value \\ 1) do
GenServer.call(counter, {:inc, value})
end
def inc_async(counter, value \\ 1) do
GenServer.cast(counter, {:inc, value})
end
def handle_call(:value, _from, state) do
{:reply, state, state}
end
def handle_call({:inc, value}, _from, state) do
state = state + value
{:reply, state, state}
end
def handle_cast({:inc, value}, state) do
state = state + value
{:noreply, state}
end
end
defmodule RegistryTest do
use ExUnit.Case
setup %{test: test_name} do
start_supervised!({Registry, name: test_name, keys: :unique})
{:ok, registry: test_name}
end
test "call w/o metadata", %{registry: registry} do
registered_name = {:via, Registry, {registry, "counter", %{meta: "data"}}}
counter_name = {:via, Registry, {registry, "counter"}}
start_supervised!({Counter, name: registered_name})
assert Counter.inc(counter_name) == 1
end
test "cast w/o metadata", %{registry: registry} do
registered_name = {:via, Registry, {registry, "counter", %{meta: "data"}}}
counter_name = {:via, Registry, {registry, "counter"}}
start_supervised!({Counter, name: registered_name})
Counter.inc_async(counter_name)
Process.sleep(100)
assert Counter.value(counter_name) == 1
end
test "call w/ metadata", %{registry: registry} do
counter_name = registered_name = {:via, Registry, {registry, "counter", %{meta: "data"}}}
start_supervised!({Counter, name: registered_name})
assert Counter.inc(counter_name) == 1
end
test "cast w/ metadata", %{registry: registry} do
counter_name = registered_name = {:via, Registry, {registry, "counter", %{meta: "data"}}}
start_supervised!({Counter, name: registered_name})
Counter.inc_async(counter_name)
Process.sleep(100)
assert Counter.value(counter_name) == 1
end
end
It has four tests, each test registers a process using a via-tuple with metadata. Two tests calls the process using GenServer.call/2
using the via-tuple name with and without metadata, another two — using GenServer.cast/2
(again, with and without metadata).
Running this snippet results in a failing test:
..
1) test cast w/ metadata (RegistryTest)
example.exs:83
Assertion with == failed
code: assert Counter.value(counter_name) == 1
left: 0
right: 1
stacktrace:
example.exs:91: (test)
.
Finished in 0.2 seconds (0.06s on load, 0.00s async, 0.2s sync)
4 tests, 1 failure
Randomized with seed 188480
"test cast w/ metadata" fails because the process haven't received the message and haven't changed its state.
Expected behavior
Casting a process should behave similar to calling a process when using the via-tuple form with metadata: so either both "test cast w/ metadata" and "test cast w/ metadata" tests should pass, or both of them should fail.
Environment
-
Elixir & Erlang/OTP versions:
Erlang/OTP 24 [erts-12.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] Elixir 1.13.3 (compiled with Erlang/OTP 24)
-
Operating system: NixOS 21.09