• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / supavisor / 22737294214

05 Mar 2026 09:20PM UTC coverage: 43.239%. First build
22737294214

Pull #865

github

web-flow
Merge f9d8e1db8 into 609d94708
Pull Request #865: fix: use a Dynamic Supervisor for starting proxy connections

5 of 84 new or added lines in 6 files covered. (5.95%)

1327 of 3069 relevant lines covered (43.24%)

3956.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/supavisor/client_handler/proxy/supervisor.ex
1
defmodule Supavisor.ClientHandler.Proxy.Supervisor do
2
  @moduledoc """
3
  Wrapping supervisor for proxy connections of a single tenant.
4

5
  Children:
6
    1. A DynamicSupervisor (`:max_children` enforces the connection limit),
7
       registered in `Supavisor.Registry.Tenants` under `{:proxy_dyn_sup, id}`.
8
    2. A watchdog (significant child) that terminates when the DynamicSupervisor
9
       is empty for some time, triggering `auto_shutdown: :any_significant` on
10
       this supervisor.
11
  """
12

13
  use Supervisor
14

15
  require Logger
16
  alias Supavisor.ClientHandler.Proxy.Watchdog
17

18
  @registry Supavisor.Registry.Tenants
19

20
  @doc """
21
  Ensures the proxy supervisor tree for the given tenant exists.
22

23
  If a DynamicSupervisor is already registered for `id`, returns `:ok`.
24
  Otherwise, starts a new proxy supervisor under the partitioned DynamicSupervisor.
25
  """
26
  @spec ensure_started(Supavisor.id(), pos_integer()) :: :ok
27
  def ensure_started(id, max_clients) do
NEW
28
    case Registry.lookup(@registry, {:proxy_dyn_sup, id}) do
×
NEW
29
      [{_, _}] ->
×
30
        :ok
31

32
      [] ->
NEW
33
        start(id, max_clients)
×
34
    end
35
  end
36

37
  defp start(id, max_clients) do
NEW
38
    case DynamicSupervisor.start_child(
×
39
           {:via, PartitionSupervisor, {Supavisor.DynamicSupervisor, id}},
40
           %{
41
             id: {:proxy_sup, id},
42
             start: {__MODULE__, :start_link, [[id: id, max_clients: max_clients]]},
43
             restart: :temporary,
44
             type: :supervisor
45
           }
46
         ) do
NEW
47
      {:ok, _} ->
×
48
        :ok
49

NEW
50
      {:error, {:shutdown, {:failed_to_start_child, _, {:already_started, _}}}} ->
×
51
        :ok
52
    end
53
  end
54

55
  @spec start_link(keyword()) :: Supervisor.on_start()
56
  def start_link(opts) do
NEW
57
    Supervisor.start_link(__MODULE__, opts)
×
58
  end
59

60
  @spec child_spec(keyword()) :: Supervisor.child_spec()
61
  def child_spec(opts) do
NEW
62
    %{
×
63
      id: __MODULE__,
64
      start: {__MODULE__, :start_link, [opts]},
65
      type: :supervisor,
66
      restart: :temporary
67
    }
68
  end
69

70
  @type start_connection_error :: :max_children | :proxy_sup_not_found | :failed_to_start
71

72
  @doc """
73
  Starts a child under the proxy DynamicSupervisor.
74

75
  Returns `{:ok, pid}` on success, or one of:
76
  - `{:error, :max_children}` if the connection limit has been reached
77
  - `{:error, :proxy_sup_not_found}` if the supervisor is not registered or has shut down
78
  - `{:error, :failed_to_start}` if the child process failed to start
79
  """
80
  @spec start_connection(Supavisor.id(), Supervisor.child_spec()) ::
81
          {:ok, pid()} | {:error, start_connection_error()}
82
  def start_connection(id, child_spec) do
NEW
83
    case Registry.lookup(@registry, {:proxy_dyn_sup, id}) do
×
84
      [{dyn_sup, _}] ->
NEW
85
        case DynamicSupervisor.start_child(dyn_sup, child_spec) do
×
NEW
86
          {:ok, pid} ->
×
87
            {:ok, pid}
88

NEW
89
          {:error, :max_children} ->
×
90
            {:error, :max_children}
91

92
          error ->
NEW
93
            Logger.error([
×
94
              "ClientHandler: failed to start proxy DbHandler: ",
95
              formatted_reason(error)
96
            ])
97

98
            {:error, :failed_to_start}
99
        end
100

NEW
101
      [] ->
×
102
        {:error, :proxy_sup_not_found}
103
    end
104
  end
105

NEW
106
  defp formatted_reason(:ignore), do: "returned ignore"
×
107

108
  defp formatted_reason({:error, error_reason}) do
NEW
109
    case error_reason do
×
110
      {exception, stacktrace} when is_exception(exception) ->
NEW
111
        Exception.format(:error, exception, stacktrace)
×
112

NEW
113
      {:bad_return_value, return_value} ->
×
114
        ["Bad return value: ", inspect(return_value)]
115

NEW
116
      value ->
×
117
        ["exited with: ", inspect(value)]
118
    end
119
  end
120

121
  @impl true
122
  def init(opts) do
NEW
123
    id = Keyword.fetch!(opts, :id)
×
NEW
124
    max_clients = Keyword.fetch!(opts, :max_clients)
×
NEW
125
    watchdog_opts = Keyword.get(opts, :watchdog_opts, [])
×
126

NEW
127
    children = [
×
128
      %{
129
        id: :connections,
130
        start:
131
          {DynamicSupervisor, :start_link,
132
           [
133
             [
134
               strategy: :one_for_one,
135
               max_children: max_clients,
136
               name: {:via, Registry, {@registry, {:proxy_dyn_sup, id}}}
137
             ]
138
           ]},
139
        type: :supervisor
140
      },
141
      Watchdog.child_spec({id, watchdog_opts})
142
    ]
143

NEW
144
    Supervisor.init(children, strategy: :one_for_one, auto_shutdown: :any_significant)
×
145
  end
146
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc