From 64d46585439d162567170e87dfe032953b33abf9 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Mon, 8 Dec 2025 15:54:21 -0300 Subject: [PATCH 01/11] added elixir tools to .gitiignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0a1640e8..51659150 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,8 @@ erl_crash.dump .elixir_ls .elixir_tools +grpc_client/.elixir-tools/ +grpc_core/.elixir-tools/ +grpc_server/.elixir-tools/ grpc-*.tar From 7bf5fe9c977ba08fccdb2e6f4d59454f56d2a1dd Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Mon, 8 Dec 2025 18:09:37 -0300 Subject: [PATCH 02/11] chore: start client supervisor in application --- grpc_client/lib/grpc/client/application.ex | 13 ++++++++++ grpc_client/lib/grpc/client/connection.ex | 28 ++++++++-------------- grpc_client/mix.exs | 1 + grpc_client/test/test_helper.exs | 7 ------ 4 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 grpc_client/lib/grpc/client/application.ex diff --git a/grpc_client/lib/grpc/client/application.ex b/grpc_client/lib/grpc/client/application.ex new file mode 100644 index 00000000..24c2d306 --- /dev/null +++ b/grpc_client/lib/grpc/client/application.ex @@ -0,0 +1,13 @@ +defmodule GRPC.Client.Application do + @moduledoc false + use Application + + def start(_type, _args) do + children = [ + {GRPC.Client.Supervisor, [name: GRPC.Client.Supervisor]} + ] + + opts = [strategy: :one_for_one, name: Grpc.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/grpc_client/lib/grpc/client/connection.ex b/grpc_client/lib/grpc/client/connection.ex index d880929c..9431a18c 100644 --- a/grpc_client/lib/grpc/client/connection.ex +++ b/grpc_client/lib/grpc/client/connection.ex @@ -153,25 +153,18 @@ defmodule GRPC.Client.Connection do """ @spec connect(String.t(), keyword()) :: {:ok, Channel.t()} | {:error, any()} def connect(target, opts \\ []) do - supervisor_pid = Process.whereis(GRPC.Client.Supervisor) + case Process.whereis(GRPC.Client.Supervisor) do + nil -> + # For compatibility purposes only. In normal operation, + # the supervisor should be started by the application. + {:ok, pid} = + DynamicSupervisor.start_link(__MODULE__, [], name: GRPC.Client.Supervisor) - 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: + pid - {:ok, _pid} = DynamicSupervisor.start_link(strategy: :one_for_one, name: GRPC.Client.Supervisor) - """ - end + pid -> + pid + end ref = make_ref() @@ -184,7 +177,6 @@ defmodule GRPC.Client.Connection do {:ok, ch} {:error, {:already_started, _pid}} -> - # race: someone else started it first, ask the running process for its current channel case pick_channel(opts) do {:ok, %Channel{} = channel} -> {:ok, channel} diff --git a/grpc_client/mix.exs b/grpc_client/mix.exs index 0fb4fcb6..7e01b3a3 100644 --- a/grpc_client/mix.exs +++ b/grpc_client/mix.exs @@ -22,6 +22,7 @@ defmodule GrpcClient.MixProject do def application do [ + mod: {GRPC.Client.Application, []}, extra_applications: [:logger] ] end diff --git a/grpc_client/test/test_helper.exs b/grpc_client/test/test_helper.exs index b98144e0..5d228d32 100644 --- a/grpc_client/test/test_helper.exs +++ b/grpc_client/test/test_helper.exs @@ -5,11 +5,4 @@ Mox.defmock(GRPC.Client.Resolver.DNS.MockAdapter, for: GRPC.Client.Resolver.DNS.Adapter ) -# Start client supervisor for integration tests -{:ok, _pid} = - DynamicSupervisor.start_link( - strategy: :one_for_one, - name: GRPC.Client.Supervisor - ) - ExUnit.start(capture_log: true) From 4ff718e8977c7cd63588535f2471787e9588daec Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Mon, 8 Dec 2025 18:12:07 -0300 Subject: [PATCH 03/11] doc: remove supervisor --- grpc_client/guides/advanced/load_balancing.md | 1 - grpc_client/guides/getting_started/client.md | 18 +----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/grpc_client/guides/advanced/load_balancing.md b/grpc_client/guides/advanced/load_balancing.md index 272470f6..a222ab20 100644 --- a/grpc_client/guides/advanced/load_balancing.md +++ b/grpc_client/guides/advanced/load_balancing.md @@ -27,7 +27,6 @@ You can connect using `DNS`, `Unix Domain sockets`, and `IPv4/IPv6` for now. ### DNS ```elixir -iex> {:ok, _pid} = GRPC.Client.Supervisor.start_link() iex> {:ok, channel} = GRPC.Stub.connect("dns://orders.prod.svc.cluster.local:50051") iex> request = Orders.GetOrderRequest.new(id: "123") iex> {:ok, reply} = channel |> Orders.OrderService.Stub.get_order(request) diff --git a/grpc_client/guides/getting_started/client.md b/grpc_client/guides/getting_started/client.md index 1d748640..41810da4 100644 --- a/grpc_client/guides/getting_started/client.md +++ b/grpc_client/guides/getting_started/client.md @@ -6,23 +6,7 @@ This section demonstrates how to establish client connections and perform RPC ca ## Basic Connection and RPC -Typically, you start this client supervisor as part of your application's supervision tree: - -```elixir -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: -```elixir -{:ok, _pid} = GRPC.Client.Supervisor.start_link() -``` - -Then connect with gRPC server: +Connect with gRPC server: ```elixir iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051") From 9fcb5ffce895f4553211a817bdea41fdd74e47c4 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Mon, 8 Dec 2025 18:15:24 -0300 Subject: [PATCH 04/11] format --- grpc_client/lib/grpc/client/connection.ex | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/grpc_client/lib/grpc/client/connection.ex b/grpc_client/lib/grpc/client/connection.ex index 9431a18c..8945a9b3 100644 --- a/grpc_client/lib/grpc/client/connection.ex +++ b/grpc_client/lib/grpc/client/connection.ex @@ -153,18 +153,18 @@ defmodule GRPC.Client.Connection do """ @spec connect(String.t(), keyword()) :: {:ok, Channel.t()} | {:error, any()} def connect(target, opts \\ []) do - case Process.whereis(GRPC.Client.Supervisor) do - nil -> - # For compatibility purposes only. In normal operation, - # the supervisor should be started by the application. - {:ok, pid} = - DynamicSupervisor.start_link(__MODULE__, [], name: GRPC.Client.Supervisor) + case Process.whereis(GRPC.Client.Supervisor) do + nil -> + # For compatibility purposes only. In normal operation, + # the supervisor should be started by the application. + {:ok, pid} = + DynamicSupervisor.start_link(__MODULE__, [], name: GRPC.Client.Supervisor) - pid + pid - pid -> - pid - end + pid -> + pid + end ref = make_ref() From 18e872784017f5d1d122e3a4509ada0ebf4df703 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Tue, 9 Dec 2025 09:52:59 -0300 Subject: [PATCH 05/11] chore: remove dead code --- benchmark/lib/benchmark/application.ex | 4 +--- grpc_client/lib/grpc/client/connection.ex | 13 ------------- interop/lib/interop/app.ex | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/benchmark/lib/benchmark/application.ex b/benchmark/lib/benchmark/application.ex index 49e7a0d6..f665f02c 100644 --- a/benchmark/lib/benchmark/application.ex +++ b/benchmark/lib/benchmark/application.ex @@ -4,9 +4,7 @@ defmodule Benchmark.Application do @impl true def start(_type, _args) do - children = [ - {GRPC.Client.Supervisor, []} - ] + children = [] opts = [strategy: :one_for_one, name: Benchmark.Supervisor] Supervisor.start_link(children, opts) diff --git a/grpc_client/lib/grpc/client/connection.ex b/grpc_client/lib/grpc/client/connection.ex index 8945a9b3..87d573a0 100644 --- a/grpc_client/lib/grpc/client/connection.ex +++ b/grpc_client/lib/grpc/client/connection.ex @@ -153,19 +153,6 @@ defmodule GRPC.Client.Connection do """ @spec connect(String.t(), keyword()) :: {:ok, Channel.t()} | {:error, any()} def connect(target, opts \\ []) do - case Process.whereis(GRPC.Client.Supervisor) do - nil -> - # For compatibility purposes only. In normal operation, - # the supervisor should be started by the application. - {:ok, pid} = - DynamicSupervisor.start_link(__MODULE__, [], name: GRPC.Client.Supervisor) - - pid - - pid -> - pid - end - ref = make_ref() case build_initial_state(target, Keyword.merge(opts, ref: ref)) do diff --git a/interop/lib/interop/app.ex b/interop/lib/interop/app.ex index 9e880ab5..f67a072e 100644 --- a/interop/lib/interop/app.ex +++ b/interop/lib/interop/app.ex @@ -3,7 +3,6 @@ defmodule Interop.App do def start(_type, _args) do children = [ - GRPC.Client.Supervisor, {GRPC.Server.Supervisor, endpoint: Interop.Endpoint, port: 10000} ] From e17c98c64f1a25eb30c5f8a2c463eb84ac8931d0 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 10:47:18 -0300 Subject: [PATCH 06/11] Update grpc_client/lib/grpc/client/application.ex Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com> --- grpc_client/lib/grpc/client/application.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grpc_client/lib/grpc/client/application.ex b/grpc_client/lib/grpc/client/application.ex index 24c2d306..6a88fef0 100644 --- a/grpc_client/lib/grpc/client/application.ex +++ b/grpc_client/lib/grpc/client/application.ex @@ -7,7 +7,7 @@ defmodule GRPC.Client.Application do {GRPC.Client.Supervisor, [name: GRPC.Client.Supervisor]} ] - opts = [strategy: :one_for_one, name: Grpc.Supervisor] + opts = [strategy: :one_for_one, name: GRPC.Supervisor] Supervisor.start_link(children, opts) end end From 3704a97cea64cfd66329875955eaf352ce158da9 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 10:47:48 -0300 Subject: [PATCH 07/11] Update .gitignore Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com> --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 51659150..8a1995dd 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,5 @@ erl_crash.dump .elixir_ls -.elixir_tools -grpc_client/.elixir-tools/ -grpc_core/.elixir-tools/ -grpc_server/.elixir-tools/ grpc-*.tar From f4d410e3f0243bd2d07c412c544ede210a584b0e Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 10:51:29 -0300 Subject: [PATCH 08/11] Update grpc_client/lib/grpc/client/application.ex Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com> --- grpc_client/lib/grpc/client/application.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grpc_client/lib/grpc/client/application.ex b/grpc_client/lib/grpc/client/application.ex index 6a88fef0..16d4a230 100644 --- a/grpc_client/lib/grpc/client/application.ex +++ b/grpc_client/lib/grpc/client/application.ex @@ -4,7 +4,7 @@ defmodule GRPC.Client.Application do def start(_type, _args) do children = [ - {GRPC.Client.Supervisor, [name: GRPC.Client.Supervisor]} + {DynamicSupervisor, [name: GRPC.Client.Supervisor]} ] opts = [strategy: :one_for_one, name: GRPC.Supervisor] From 5daf08a6dc428c16781fe8ef0f355223bc755b88 Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 11:02:28 -0300 Subject: [PATCH 09/11] remove bench application.ex --- benchmark/lib/benchmark/application.ex | 12 ------------ benchmark/mix.exs | 1 - 2 files changed, 13 deletions(-) delete mode 100644 benchmark/lib/benchmark/application.ex diff --git a/benchmark/lib/benchmark/application.ex b/benchmark/lib/benchmark/application.ex deleted file mode 100644 index f665f02c..00000000 --- a/benchmark/lib/benchmark/application.ex +++ /dev/null @@ -1,12 +0,0 @@ -defmodule Benchmark.Application do - @moduledoc false - use Application - - @impl true - def start(_type, _args) do - children = [] - - opts = [strategy: :one_for_one, name: Benchmark.Supervisor] - Supervisor.start_link(children, opts) - end -end diff --git a/benchmark/mix.exs b/benchmark/mix.exs index d6521360..3e0ac018 100644 --- a/benchmark/mix.exs +++ b/benchmark/mix.exs @@ -15,7 +15,6 @@ defmodule Benchmark.MixProject do def application do [ extra_applications: [:logger], - mod: {Benchmark.Application, []} ] end From 64f90ce603ee7b5d083bb6ba7fd58c9510e3409f Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 11:13:56 -0300 Subject: [PATCH 10/11] fix: remove client supervisor --- grpc_client/lib/grpc/client/supervisor.ex | 63 ----------------------- 1 file changed, 63 deletions(-) delete mode 100644 grpc_client/lib/grpc/client/supervisor.ex diff --git a/grpc_client/lib/grpc/client/supervisor.ex b/grpc_client/lib/grpc/client/supervisor.ex deleted file mode 100644 index ba2d53ed..00000000 --- a/grpc_client/lib/grpc/client/supervisor.ex +++ /dev/null @@ -1,63 +0,0 @@ -defmodule GRPC.Client.Supervisor do - @moduledoc """ - A DynamicSupervisor responsible for managing gRPC client connections (`GRPC.Client.Connection`). - - This supervisor allows you to dynamically start and stop gRPC client connections at runtime. - Each connection is run as a separate `GenServer` under this supervisor, which ensures proper - supervision and isolation between connections. - - ## Starting the Supervisor - - Typically, you start this supervisor 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} = GRPC.Client.Supervisor.start_link([]) - - ## Supervision Strategy - - This supervisor uses `:one_for_one` strategy: - - * If a connection process crashes, only that process is restarted. - * Other running connections remain unaffected. - - ## Establishing a gRPC Connection - - To create a new gRPC connection, you typically use the `GRPC.Stub.connect/1` function, - which internally starts a `GRPC.Client.Connection` process under this supervisor. For example: - - iex> {:ok, ch} = GRPC.Stub.connect("127.0.0.1:50051") - iex> Grpc.Testing.TestService.Stub.empty_call(ch, %{}) - - ## Notes - - * You can dynamically start multiple connections under the supervisor for different targets. - * Each connection runs in isolation as its own GenServer. - """ - use DynamicSupervisor - - def start_link(opts) do - case DynamicSupervisor.start_link(__MODULE__, opts, name: __MODULE__) do - {:ok, _pid} = started -> - started - - {:error, {:already_started, pid}} -> - {:ok, pid} - - other -> - other - end - end - - @impl true - def init(_opts) do - DynamicSupervisor.init(strategy: :one_for_one) - end -end From a81fe1f28e9422946571ff29dfaf11e8151f67bc Mon Sep 17 00:00:00 2001 From: Adriano Santos Date: Fri, 12 Dec 2025 11:16:40 -0300 Subject: [PATCH 11/11] remove supervisor from tests --- grpc_client/test/grpc/supervisor_test.exs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 grpc_client/test/grpc/supervisor_test.exs diff --git a/grpc_client/test/grpc/supervisor_test.exs b/grpc_client/test/grpc/supervisor_test.exs deleted file mode 100644 index cd83c871..00000000 --- a/grpc_client/test/grpc/supervisor_test.exs +++ /dev/null @@ -1,14 +0,0 @@ -defmodule GRPC.Client.SupervisorTest do - use ExUnit.Case, async: false - - alias GRPC.Client - - describe "start_link/1" do - test "allows multiple start_links" do - {:ok, second_pid} = Client.Supervisor.start_link([]) - {:ok, third_pid} = Client.Supervisor.start_link([]) - - assert second_pid == third_pid - end - end -end