From e3d5cc721bc66409852be5c71017648a29316640 Mon Sep 17 00:00:00 2001 From: Micah Cooper Date: Fri, 23 Jan 2026 11:50:38 -0600 Subject: [PATCH 1/3] Support naming a virtual channel Allow a GRPC.Channel to be created with a name. This removes the need for developers to store the `Channel` that's returned from `GRPC.Client.Connection.connect`. --- grpc_client/lib/grpc/client/connection.ex | 26 ++++++++++++++++--- .../test/grpc/integration/stub_test.exs | 15 +++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/grpc_client/lib/grpc/client/connection.ex b/grpc_client/lib/grpc/client/connection.ex index 98af91d97..a80a7dba8 100644 --- a/grpc_client/lib/grpc/client/connection.ex +++ b/grpc_client/lib/grpc/client/connection.ex @@ -153,9 +153,27 @@ defmodule GRPC.Client.Connection do """ @spec connect(String.t(), keyword()) :: {:ok, Channel.t()} | {:error, any()} def connect(target, opts \\ []) do - ref = make_ref() + supervisor_pid = Process.whereis(GRPC.Client.Supervisor) - case build_initial_state(target, Keyword.merge(opts, ref: ref)) do + if is_nil(supervisor_pid) or !Process.alive?(supervisor_pid) do + raise """ + GRPC.Client.Supervisor is not running. Please ensure it is started as part of your + application's supervision tree: + + children = [ + {GRPC.Client.Supervisor, []} + ] + + opts = [strategy: :one_for_one, name: MyApp.Supervisor] + Supervisor.start_link(children, opts) + + You can also start it manually in scripts or test environments: + + {:ok, _pid} = DynamicSupervisor.start_link(strategy: :one_for_one, name: GRPC.Client.Supervisor) + """ + end + + case build_initial_state(target, opts) do {:ok, initial_state} -> ch = initial_state.virtual_channel @@ -307,7 +325,7 @@ defmodule GRPC.Client.Connection do opts = Keyword.validate!(opts, cred: nil, - ref: nil, + name: make_ref(), adapter: GRPC.Client.Adapters.Gun, adapter_opts: [], interceptors: [], @@ -333,7 +351,7 @@ defmodule GRPC.Client.Connection do virtual_channel = %Channel{ scheme: scheme, cred: cred, - ref: opts[:ref], + ref: opts[:name], adapter: adapter, interceptors: interceptors, codec: norm_opts[:codec], diff --git a/grpc_client/test/grpc/integration/stub_test.exs b/grpc_client/test/grpc/integration/stub_test.exs index bea07283f..bc59daa00 100644 --- a/grpc_client/test/grpc/integration/stub_test.exs +++ b/grpc_client/test/grpc/integration/stub_test.exs @@ -68,6 +68,21 @@ defmodule GRPC.Integration.StubTest do end) end + test "use a channel name to send a message" do + run_server(HelloServer, fn port -> + {:ok, _channel} = + GRPC.Client.Connection.connect("localhost:#{port}", + interceptors: [GRPC.Client.Interceptors.Logger], + name: :foobar + ) + + name = "GRPC user!" + req = %Helloworld.HelloRequest{name: name} + {:ok, reply} = %GRPC.Channel{ref: :foobar} |> Helloworld.Greeter.Stub.say_hello(req) + assert reply.message == "Hello, #{name}" + end) + end + test "invalid channel function clause error" do req = %Helloworld.HelloRequest{name: "GRPC"} From 866ae0b1d7a9183fb777738e3e6f62221c7233ed Mon Sep 17 00:00:00 2001 From: Micah Cooper Date: Fri, 23 Jan 2026 13:50:25 -0600 Subject: [PATCH 2/3] Exemplify test with module attribute channel name --- grpc_client/test/grpc/integration/stub_test.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grpc_client/test/grpc/integration/stub_test.exs b/grpc_client/test/grpc/integration/stub_test.exs index bc59daa00..488d46552 100644 --- a/grpc_client/test/grpc/integration/stub_test.exs +++ b/grpc_client/test/grpc/integration/stub_test.exs @@ -68,17 +68,18 @@ defmodule GRPC.Integration.StubTest do end) end + @channel_name :my_channel test "use a channel name to send a message" do run_server(HelloServer, fn port -> {:ok, _channel} = GRPC.Client.Connection.connect("localhost:#{port}", interceptors: [GRPC.Client.Interceptors.Logger], - name: :foobar + name: @channel_name ) name = "GRPC user!" req = %Helloworld.HelloRequest{name: name} - {:ok, reply} = %GRPC.Channel{ref: :foobar} |> Helloworld.Greeter.Stub.say_hello(req) + {:ok, reply} = %GRPC.Channel{ref: channel_name} |> Helloworld.Greeter.Stub.say_hello(req) assert reply.message == "Hello, #{name}" end) end From 31967e704fdb661501a96f3c8d52cb5058cfbe6b Mon Sep 17 00:00:00 2001 From: Micah Cooper <698595+mrmicahcooper@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:25:09 -0600 Subject: [PATCH 3/3] Don't use module attribute to exemplify channel name Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com> --- grpc_client/test/grpc/integration/stub_test.exs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grpc_client/test/grpc/integration/stub_test.exs b/grpc_client/test/grpc/integration/stub_test.exs index 488d46552..255a74ba7 100644 --- a/grpc_client/test/grpc/integration/stub_test.exs +++ b/grpc_client/test/grpc/integration/stub_test.exs @@ -68,18 +68,17 @@ defmodule GRPC.Integration.StubTest do end) end - @channel_name :my_channel test "use a channel name to send a message" do run_server(HelloServer, fn port -> {:ok, _channel} = GRPC.Client.Connection.connect("localhost:#{port}", interceptors: [GRPC.Client.Interceptors.Logger], - name: @channel_name + name: :my_channel ) name = "GRPC user!" req = %Helloworld.HelloRequest{name: name} - {:ok, reply} = %GRPC.Channel{ref: channel_name} |> Helloworld.Greeter.Stub.say_hello(req) + {:ok, reply} = %GRPC.Channel{ref: :my_channel} |> Helloworld.Greeter.Stub.say_hello(req) assert reply.message == "Hello, #{name}" end) end