@anthonator Just dropping by to say that your approach inspired me to figure out something similar.
I did just slightly differently without a new connection pool module (and thus no delegations).
defmodule DB.SharedConnectionPool do
alias DBConnection.ConnectionPool
def child_spec({mod, opts}) do
opts = Keyword.put_new(opts, :name, pool_name(opts))
Supervisor.Spec.worker(__MODULE__, [{mod, opts}])
end
def start_link({mod, opts}) do
case GenServer.start_link(ConnectionPool, {mod, opts}, start_opts(opts)) do
{:ok, pid} -> {:ok, pid}
{:error, {:already_started, pid}} -> {:ok, pid}
error -> error
end
end
defp pool_name(opts) do
case Keyword.fetch(opts, :shared_pool_id) do
{:ok, pool} -> String.to_atom("#{__MODULE__}-#{pool}")
:error -> __MODULE__
end
end
# Exact same as `DBConnection.ConnectionPool`
defp start_opts(opts) do
Keyword.take(opts, [:name, :spawn_opt])
end
end
And I have the repos configured like this:
config :my_app, MyApp.Repo,
pool: DB.SharedConnectionPool,
shared_pool_id: :my_shared_pool_id
config :my_second_app, MySecondApp.Repo,
pool: DB.SharedConnectionPool,
shared_pool_id: :my_shared_pool_id
This way makes it also possible to specifically configure what repos should share a pool, and multiple shared pools can be created.
The downside is that it still relies on the connection info from the configs, which means if two repos with different configs share the same pool, there’s a race condition and one repo config will be taken to start the pool. But otherwise this approach has the least impact on how Ecto works.
Full code here: https://github.com/omisego/ewallet/pull/853 (disclaimer: I’m employed by this project)