diff --git a/integration_test/cases/idle_test.exs b/integration_test/cases/idle_test.exs index 9687fc17..d10342de 100644 --- a/integration_test/cases/idle_test.exs +++ b/integration_test/cases/idle_test.exs @@ -28,7 +28,7 @@ defmodule TestIdle do end] {:ok, agent} = A.start_link(stack) - opts = [agent: agent, parent: self(), idle_timeout: 50] + opts = [agent: agent, parent: self(), idle_timeout: 50, idle_interval: 50] {:ok, pool} = P.start_link(opts) assert_receive {:hi, conn} assert_receive {:pong, ^conn} diff --git a/integration_test/cases/queue_test.exs b/integration_test/cases/queue_test.exs index 0383ace6..6987dcdd 100644 --- a/integration_test/cases/queue_test.exs +++ b/integration_test/cases/queue_test.exs @@ -42,7 +42,8 @@ defmodule QueueTest do stack = [{:ok, :state}] {:ok, agent} = A.start_link(stack) - opts = [agent: agent, parent: self(), queue_timeout: 50] + opts = [agent: agent, parent: self(), queue_timeout: 50, queue_target: 50, + queue_interval: 50] {:ok, pool} = P.start_link(opts) parent = self() @@ -147,7 +148,8 @@ defmodule QueueTest do {:ok, agent} = A.start_link(stack) opts = [agent: agent, parent: self(), backoff_start: 30_000, - queue_timeout: 10, pool_timeout: 10] + queue_timeout: 10, pool_timeout: 10, queue_target: 10, + queue_interval: 10] {:ok, pool} = P.start_link(opts) P.run(pool, fn(_) -> @@ -165,7 +167,7 @@ defmodule QueueTest do {:ok, agent} = A.start_link(stack) opts = [agent: agent, parent: self(), backoff_start: 30_000, - queue_timeout: 10, pool_timeout: 10] + queue_timeout: 10, pool_timeout: 10, queue_target: 10, queue_interval: 10] {:ok, pool} = P.start_link(opts) P.run(pool, fn(_) -> diff --git a/integration_test/connection_pool/all_test.exs b/integration_test/connection_pool/all_test.exs new file mode 100644 index 00000000..cba57bb6 --- /dev/null +++ b/integration_test/connection_pool/all_test.exs @@ -0,0 +1 @@ +Code.require_file "../tests.exs", __DIR__ \ No newline at end of file diff --git a/integration_test/connection_pool/test_helper.exs b/integration_test/connection_pool/test_helper.exs new file mode 100644 index 00000000..14d35d74 --- /dev/null +++ b/integration_test/connection_pool/test_helper.exs @@ -0,0 +1,11 @@ +ExUnit.start([capture_log: true, assert_receive_timeout: 500, + exclude: [:pool_overflow, :enqueue_disconnected, + :queue_timeout_exit]]) + +Code.require_file "../../test/test_support.exs", __DIR__ + +defmodule TestPool do + use TestConnection, [pool: DBConnection.ConnectionPool, pool_size: 1] +end + +{:ok, _} = TestPool.ensure_all_started() \ No newline at end of file diff --git a/lib/db_connection/app.ex b/lib/db_connection/app.ex index 54aabcc2..0ae4526f 100644 --- a/lib/db_connection/app.ex +++ b/lib/db_connection/app.ex @@ -9,6 +9,7 @@ defmodule DBConnection.App do supervisor(DBConnection.Task, []), supervisor(DBConnection.Sojourn.Supervisor, []), supervisor(DBConnection.Ownership.PoolSupervisor, []), + supervisor(DBConnection.ConnectionPool.PoolSupervisor, []), worker(DBConnection.Watcher, []) ] Supervisor.start_link(children, strategy: :one_for_all, name: __MODULE__) diff --git a/lib/db_connection/connection.ex b/lib/db_connection/connection.ex index 708a2c5b..ae9780dd 100644 --- a/lib/db_connection/connection.ex +++ b/lib/db_connection/connection.ex @@ -17,6 +17,7 @@ defmodule DBConnection.Connection do use Connection require Logger alias DBConnection.Backoff + alias DBConnection.ConnectionPool @pool_timeout 5_000 @timeout 15_000 @@ -83,6 +84,11 @@ defmodule DBConnection.Connection do end end + @doc false + def ping({pid, ref}, state) do + Connection.cast(pid, {:ping, ref, state}) + end + ## Internal API @doc false @@ -96,19 +102,26 @@ defmodule DBConnection.Connection do Supervisor.Spec.worker(__MODULE__, [mod, opts, mode], child_opts) end + @doc false + def child_spec(mod, opts, mode, info, child_opts) do + Supervisor.Spec.worker(__MODULE__, [mod, opts, mode, info], child_opts) + end + ## Connection API @doc false def init({mod, opts, mode, info}) do - queue = if mode == :sojourn, do: :broker, else: :queue.new() + queue = if mode in [:connection, :poolboy], do: :queue.new(), else: mode + idle = if mode in [:connection, :poolboy], do: get_idle(opts), else: :passive broker = if mode == :sojourn, do: elem(info, 0) regulator = if mode == :sojourn, do: elem(info, 1) - idle = if mode == :sojourn, do: :passive, else: get_idle(opts) after_timeout = if mode == :poolboy, do: :stop, else: :backoff + pool = if mode == :connection_pool, do: elem(info, 0) + tag = if mode == :connection_pool, do: elem(info, 1) s = %{mod: mod, opts: opts, state: nil, client: :closed, broker: broker, - regulator: regulator, lock: nil, queue: queue, timer: nil, - backoff: Backoff.new(opts), + regulator: regulator, lock: nil, pool: pool, tag: tag, queue: queue, + timer: nil, backoff: Backoff.new(opts), after_connect: Keyword.get(opts, :after_connect), after_connect_timeout: Keyword.get(opts, :after_connect_timeout, @timeout), idle: idle, @@ -191,7 +204,7 @@ defmodule DBConnection.Connection do @doc false def handle_call({:checkout, ref, queue?, timeout}, {pid, _} = from, s) do case s do - %{queue: :broker} -> + %{queue: mode} when is_atom(mode) -> exit(:bad_checkout) %{client: nil, idle: :passive, mod: mod, state: state} -> Connection.reply(from, {:ok, {self(), ref}, mod, state}) @@ -229,6 +242,16 @@ defmodule DBConnection.Connection do end @doc false + def handle_cast({:ping, ref, state}, %{client: {ref, :pool}} = s) do + %{mod: mod} = s + case apply(mod, :ping, [state]) do + {:ok, state} -> + pool_update(state, s) + {:disconnect, err, state} -> + {:disconnect, {:log, err}, %{s | state: state}} + end + end + def handle_cast({:checkin, ref, state}, %{client: {ref, _}} = s) do handle_next(state, s) end @@ -244,7 +267,7 @@ defmodule DBConnection.Connection do {:stop, {err, stack}, %{s | state: state}} end - def handle_cast({:cancel, _}, %{queue: :broker}) do + def handle_cast({:cancel, _}, %{queue: mode}) when is_atom(mode) do exit(:bad_cancel) end def handle_cast({:cancel, ref}, %{client: {ref, _}, state: state} = s) do @@ -293,7 +316,9 @@ defmodule DBConnection.Connection do def handle_cast({:connected, ref}, %{client: {ref, :connect}} = s) do %{mod: mod, state: state, queue: queue, broker: broker} = s case apply(mod, :checkout, [state]) do - {:ok, state} when queue == :broker -> + {:ok, state} when queue == :connection_pool -> + pool_update(state, s) + {:ok, state} when queue == :sojourn -> info = {self(), mod, state} {:await, ^ref, _} = :sbroker.async_ask_r(broker, info, {self(), ref}) {:noreply, %{s | client: {ref, :broker}, state: state}} @@ -320,7 +345,8 @@ defmodule DBConnection.Connection do err = DBConnection.ConnectionError.exception(message) {:disconnect, {down_log(reason), err}, %{s | client: {ref, nil}}} end - def handle_info({:DOWN, _, :process, _, _} = msg, %{queue: :broker} = s) do + def handle_info({:DOWN, _, :process, _, _} = msg, %{queue: mode} = s) + when is_atom(mode) do do_handle_info(msg, s) end def handle_info({:DOWN, ref, :process, _, _} = msg, %{queue: queue} = s) do @@ -410,7 +436,7 @@ defmodule DBConnection.Connection do defp start_opts(:connection, opts) do Keyword.take(opts, [:debug, :name, :timeout, :spawn_opt]) end - defp start_opts(mode, opts) when mode in [:poolboy, :sojourn] do + defp start_opts(mode, opts) when mode in [:poolboy, :sojourn, :connection_pool] do Keyword.take(opts, [:debug, :spawn_opt]) end @@ -460,7 +486,10 @@ defmodule DBConnection.Connection do demonitor(client) handle_next(state, %{s | client: nil, backoff: backoff}) end - defp handle_next(state, %{queue: :broker} = s) do + defp handle_next(state, %{queue: :connection_pool} = s) do + pool_update(state, s) + end + defp handle_next(state, %{queue: :sojourn} = s) do %{client: client, timer: timer, mod: mod, broker: broker} = s demonitor(client) cancel_timer(timer) @@ -600,7 +629,9 @@ defmodule DBConnection.Connection do end end - defp clear_queue(:broker), do: :broker + defp clear_queue(queue) when is_atom(queue) do + queue + end defp clear_queue(queue) do clear = fn({{_, mon}, _, from}) -> @@ -619,6 +650,11 @@ defmodule DBConnection.Connection do :ok end + defp pool_update(state, %{pool: pool, tag: tag, mod: mod} = s) do + ref = ConnectionPool.update(pool, tag, mod, state) + {:noreply, %{s | client: {ref, :pool}, state: state}, :hibernate} + end + defp normal_status(mod, pdict, state) do try do mod.format_status(:normal, [pdict, state]) diff --git a/lib/db_connection/connection_pool.ex b/lib/db_connection/connection_pool.ex new file mode 100644 index 00000000..0cf069c4 --- /dev/null +++ b/lib/db_connection/connection_pool.ex @@ -0,0 +1,428 @@ +defmodule DBConnection.ConnectionPool do + + @behaviour DBConnection.Pool + use GenServer + alias DBConnection.ConnectionPool.PoolSupervisor + + @timeout 5000 + @queue true + @queue_target 50 + @queue_interval 1000 + @idle_interval 1000 + @time_unit 1000 + @holder_key :__info__ + + ## DBConnection.Pool API + + @doc false + def ensure_all_started(_opts, _type) do + {:ok, []} + end + + @doc false + def start_link(mod, opts) do + GenServer.start_link(__MODULE__, {mod, opts}, start_opts(opts)) + end + + @doc false + def child_spec(mod, opts, child_opts \\ []) do + Supervisor.Spec.worker(__MODULE__, [mod, opts], child_opts) + end + + @doc false + def checkout(pool, opts) do + queue? = Keyword.get(opts, :queue, @queue) + now = System.monotonic_time(@time_unit) + timeout = abs_timeout(now, opts) + case checkout(pool, queue?, now, timeout) do + {:ok, _, _, _} = ok -> + ok + {:error, _} = error -> + error + {:exit, reason} -> + exit({reason, {__MODULE__, :checkout, [pool, opts]}}) + end + end + + @doc false + def checkin({pool, ref, deadline, holder}, conn, _) do + cancel_deadline(deadline) + now = System.monotonic_time(@time_unit) + checkin_holder(holder, pool, conn, {:checkin, ref, now}) + end + + @doc false + def disconnect({pool, ref, deadline, holder}, err, conn, _) do + cancel_deadline(deadline) + checkin_holder(holder, pool, conn, {:disconnect, ref, err}) + end + + @doc false + def stop({pool, ref, deadline, holder}, err, conn, _) do + cancel_deadline(deadline) + checkin_holder(holder, pool, conn, {:stop, ref, err}) + end + + ## Holder api + + @doc false + def update(pool, ref, mod, state) do + holder = start_holder(pool, ref, mod, state) + now = System.monotonic_time(@time_unit) + checkin_holder(holder, pool, state, {:checkin, ref, now}) + holder + end + + ## GenServer api + + def init({mod, opts}) do + queue = :ets.new(__MODULE__.Queue, [:private, :ordered_set]) + {:ok, _} = PoolSupervisor.start_pool(queue, mod, opts) + target = Keyword.get(opts, :queue_target, @queue_target) + interval = Keyword.get(opts, :queue_interval, @queue_interval) + idle_interval = Keyword.get(opts, :idle_interval, @idle_interval) + now = System.monotonic_time(@time_unit) + codel = %{target: target, interval: interval, delay: 0, slow: false, + next: now, poll: nil, idle_interval: idle_interval, idle: nil} + codel = start_idle(now, now, start_poll(now, now, codel)) + {:ok, {:busy, queue, codel}} + end + + def handle_info({:db_connection, from, {:checkout, now, queue?}}, {:busy, queue, _} = busy) do + case queue? do + true -> + :ets.insert(queue, {{now, System.unique_integer(), from}}) + {:noreply, busy} + false -> + message = "connection not available and queuing is disabled" + err = DBConnection.ConnectionError.exception(message) + GenServer.reply(from, {:error, err}) + {:noreply, busy} + end + end + + def handle_info({:db_connection, from, {:checkout, _now, _queue?}} = checkout, ready) do + {:ready, queue, _codel} = ready + case :ets.first(queue) do + {_time, holder} = key -> + checkout_holder(holder, from, queue) and :ets.delete(queue, key) + {:noreply, ready} + :"$end_of_table" -> + handle_info(checkout, put_elem(ready, 0, :busy)) + end + end + + def handle_info({:"ETS-TRANSFER", holder, pid, queue}, {_, queue, _} = data) do + message = "client #{inspect pid} exited" + err = DBConnection.ConnectionError.exception(message) + disconnect_holder(holder, err) + {:noreply, data} + end + + def handle_info({:"ETS-TRANSFER", holder, _, {msg, queue, extra}}, {_, queue, _} = data) do + case msg do + :checkin -> + handle_checkin(holder, extra, data) + :disconnect -> + disconnect_holder(holder, extra) + {:noreply, data} + :stop -> + stop_holder(holder, extra) + {:noreply, data} + end + end + + def handle_info({:timeout, deadline, {queue, holder, pid, len}}, {_, queue, _} = data) do + # Check that timeout refers to current holder (and not previous) + try do + :ets.lookup_element(holder, @holder_key, 3) + rescue + ArgumentError -> + :ok + else + ^deadline -> + :ets.update_element(holder, @holder_key, {3, nil}) + message = "client #{inspect pid} timed out because " <> + "it queued and checked out the connection for longer than #{len}ms" + err = DBConnection.ConnectionError.exception(message) + disconnect_holder(holder, err) + _ -> + :ok + end + {:noreply, data} + end + + def handle_info({:timeout, poll, {time, last_sent}}, {_, _, %{poll: poll}} = data) do + {status, queue, codel} = data + # If no queue progress since last poll check queue + case :ets.first(queue) do + {sent, _, _} when sent <= last_sent and status == :busy -> + delay = time - sent + timeout(delay, time, queue, start_poll(time, sent, codel)) + {sent, _, _} -> + {:noreply, {status, queue, start_poll(time, sent, codel)}} + _ -> + {:noreply, {status, queue, start_poll(time, time, codel)}} + end + end + + def handle_info({:timeout, idle, {time, last_sent}}, {_, _, %{idle: idle}} = data) do + {status, queue, codel} = data + # If no queue progress since last idle check oldest connection + case :ets.first(queue) do + {sent, holder} = key when sent <= last_sent and status == :ready -> + :ets.delete(queue, key) + ping(holder, queue, start_idle(time, last_sent, codel)) + {sent, _} -> + {:noreply, {status, queue, start_idle(time, sent, codel)}} + _ -> + {:noreply, {status, queue, start_idle(time, time, codel)}} + end + end + + defp timeout(delay, time, queue, codel) do + case codel do + %{delay: min_delay, next: next, target: target, interval: interval} + when time >= next and min_delay > target -> + codel = %{codel | slow: true, delay: delay, next: time + interval} + drop_slow(time, target * 2, queue) + {:noreply, {:busy, queue, codel}} + %{next: next, interval: interval} when time >= next -> + codel = %{codel | slow: false, delay: delay, next: time + interval} + {:noreply, {:busy, queue, codel}} + _ -> + {:noreply, {:busy, queue, codel}} + end + end + + defp drop_slow(time, timeout, queue) do + min_sent = time - timeout + match = {{:"$1", :_, :"$2"}} + guards = [{:<, :"$1", min_sent}] + select_slow = [{match, guards, [{{:"$1", :"$2"}}]}] + for {sent, from} <- :ets.select(queue, select_slow) do + drop(time - sent, from) + end + :ets.select_delete(queue, [{match, guards, [true]}]) + end + + defp ping(holder, queue, codel) do + [{_, conn, _, _, state}] = :ets.lookup(holder, @holder_key) + DBConnection.Connection.ping({conn, holder}, state) + :ets.delete(holder) + {:noreply, {:ready, queue, codel}} + end + + defp handle_checkin(holder, now, {:ready, queue, _} = data) do + :ets.insert(queue, {{now, holder}}) + {:noreply, data} + end + + defp handle_checkin(holder, now, {:busy, queue, codel}) do + dequeue(now, holder, queue, codel) + end + + defp dequeue(time, holder, queue, codel) do + case codel do + %{next: next, delay: delay, target: target} when time >= next -> + dequeue_first(time, delay > target, holder, queue, codel) + %{slow: false} -> + dequeue_fast(time, holder, queue, codel) + %{slow: true, target: target} -> + dequeue_slow(time, target * 2, holder, queue, codel) + end + end + + defp dequeue_first(time, slow?, holder, queue, codel) do + %{interval: interval} = codel + next = time + interval + case :ets.first(queue) do + {sent, _, from} = key -> + :ets.delete(queue, key) + delay = time - sent + codel = %{codel | next: next, delay: delay, slow: slow?} + go(delay, from, time, holder, queue, codel) + :"$end_of_table" -> + codel = %{codel | next: next, delay: 0, slow: slow?} + :ets.insert(queue, {{time, holder}}) + {:noreply, {:ready, queue, codel}} + end + end + + defp dequeue_fast(time, holder, queue, codel) do + case :ets.first(queue) do + {sent, _, from} = key -> + :ets.delete(queue, key) + go(time - sent, from, time, holder, queue, codel) + :"$end_of_table" -> + :ets.insert(queue, {{time, holder}}) + {:noreply, {:ready, queue, %{codel | delay: 0}}} + end + end + + defp dequeue_slow(time, timeout, holder, queue, codel) do + case :ets.first(queue) do + {sent, _, from} = key when time - sent > timeout -> + :ets.delete(queue, key) + drop(time - sent, from) + dequeue_slow(time, timeout, holder, queue, codel) + {sent, _, from} = key -> + :ets.delete(queue, key) + go(time - sent, from, time, holder, queue, codel) + :"$end_of_table" -> + :ets.insert(queue, {{time, holder}}) + {:noreply, {:ready, queue, %{codel | delay: 0}}} + end + end + + defp go(delay, from, time, holder, queue, %{delay: min} = codel) do + case checkout_holder(holder, from, queue) do + true when delay < min -> + {:noreply, {:busy, queue, %{codel | delay: delay}}} + true -> + {:noreply, {:busy, queue, codel}} + false -> + dequeue(time, holder, queue, codel) + end + end + + defp drop(delay, from) do + message = "connection not available " <> + "and request was dropped from queue after #{delay}ms" + err = DBConnection.ConnectionError.exception(message) + GenServer.reply(from, {:error, err}) + end + + defp start_opts(opts) do + Keyword.take(opts, [:name, :spawn_opt]) + end + + defp abs_timeout(now, opts) do + case Keyword.get(opts, :timeout, @timeout) do + :infinity -> + Keyword.get(opts, :deadline) + timeout -> + min(now + timeout, Keyword.get(opts, :deadline)) + end + end + + defp start_deadline(nil, _, _, _, _) do + nil + end + defp start_deadline(timeout, pid, ref, holder, start) do + deadline = :erlang.start_timer(timeout, pid, {ref, holder, self(), timeout-start}, [abs: true]) + :ets.update_element(holder, @holder_key, {3, deadline}) + deadline + end + + defp cancel_deadline(nil) do + :ok + end + + defp cancel_deadline(deadline) do + :erlang.cancel_timer(deadline, [async: true, info: false]) + end + + defp start_poll(now, last_sent, %{interval: interval} = codel) do + timeout = now + interval + poll = :erlang.start_timer(timeout, self(), {timeout, last_sent}, [abs: true]) + %{codel | poll: poll} + end + + defp start_idle(now, last_sent, %{idle_interval: interval} = codel) do + timeout = now + interval + idle = :erlang.start_timer(timeout, self(), {timeout, last_sent}, [abs: true]) + %{codel | idle: idle} + end + + defp start_holder(pool, ref, mod, state) do + # Insert before setting heir so that pool can't receive empty table + holder = :ets.new(__MODULE__.Holder, [:public, :ordered_set]) + :true = :ets.insert_new(holder, {@holder_key, self(), nil, mod, state}) + :ets.setopts(holder, {:heir, pool, ref}) + holder + end + + defp checkout_holder(holder, {pid, mref}, ref) do + try do + :ets.give_away(holder, pid, {mref, ref}) + rescue + ArgumentError -> + # pid is not alive + false + end + end + + defp checkout(pool, queue?, start, timeout) do + case GenServer.whereis(pool) do + pid when node(pid) == node() -> + checkout_call(pid, queue?, start, timeout) + pid when node(pid) != node() -> + {:exit, {:badnode, node(pid)}} + {_name, node} -> + {:exit, {:badnode, node}} + nil -> + {:exit, :noproc} + end + end + + defp checkout_call(pid, queue?, start, timeout) do + mref = Process.monitor(pid) + send(pid, {:db_connection, {self(), mref}, {:checkout, start, queue?}}) + receive do + {:"ETS-TRANSFER", holder, owner, {^mref, ref}} -> + Process.demonitor(mref, [:flush]) + deadline = start_deadline(timeout, owner, ref, holder, start) + pool_ref = {owner, ref, deadline, holder} + checkout_info(holder, pool_ref) + {^mref, reply} -> + Process.demonitor(mref, [:flush]) + reply + {:DOWN, ^mref, _, _, reason} -> + {:exit, reason} + end + end + + defp checkout_info(holder, pool_ref) do + try do + :ets.lookup(holder, @holder_key) + rescue + ArgumentError -> + # Deadline could hit and by handled pool before using connection + msg = "connection not available because deadline reached while in queue" + {:error, DBConnection.ConnectionError.exception(msg)} + else + [{_, _, _, mod, state}] -> + {:ok, pool_ref, mod, state} + end + end + + defp checkin_holder(holder, pool, state, msg) do + try do + :ets.update_element(holder, @holder_key, [{3, nil}, {5, state}]) + :ets.give_away(holder, pool, msg) + rescue + ArgumentError -> + :ok + else + true -> + :ok + end + end + + defp disconnect_holder(holder, err) do + delete_holder(holder, &DBConnection.Connection.disconnect/4, err) + end + + defp stop_holder(holder, err) do + delete_holder(holder, &DBConnection.Connection.stop/4, err) + end + + defp delete_holder(holder, stop, err) do + [{_, conn, deadline, _, state}] = :ets.lookup(holder, @holder_key) + :ets.delete(holder) + cancel_deadline(deadline) + stop.({conn, holder}, err, state, []) + end +end + \ No newline at end of file diff --git a/lib/db_connection/connection_pool/pool.ex b/lib/db_connection/connection_pool/pool.ex new file mode 100644 index 00000000..cc01a729 --- /dev/null +++ b/lib/db_connection/connection_pool/pool.ex @@ -0,0 +1,17 @@ +defmodule DBConnection.ConnectionPool.Pool do + @moduledoc false + + def start_link(owner, tag, mod, opts) do + size = Keyword.get(opts, :pool_size, 20) + children = for id <- 1..size, do: conn(owner, tag, id, mod, opts) + sup_opts = [strategy: :one_for_one] ++ Keyword.take(opts, [:max_restarts, :max_seconds]) + Supervisor.start_link(children, sup_opts) + end + + ## Helpers + + defp conn(owner, tag, id, mod, opts) do + child_opts = [id: {mod, owner, id}] ++ Keyword.take(opts, [:shutdown]) + DBConnection.Connection.child_spec(mod, opts, :connection_pool, {owner, tag}, child_opts) + end +end diff --git a/lib/db_connection/connection_pool/pool_supervisor.ex b/lib/db_connection/connection_pool/pool_supervisor.ex new file mode 100644 index 00000000..cdae617b --- /dev/null +++ b/lib/db_connection/connection_pool/pool_supervisor.ex @@ -0,0 +1,16 @@ +defmodule DBConnection.ConnectionPool.PoolSupervisor do + @moduledoc false + + alias DBConnection.ConnectionPool.Pool + import Supervisor.Spec + + def start_link() do + pool = supervisor(Pool, [], [restart: :temporary]) + sup_opts = [strategy: :simple_one_for_one, name: __MODULE__] + Supervisor.start_link([pool], sup_opts) + end + + def start_pool(tag, mod, opts) do + DBConnection.Watcher.watch(__MODULE__, [self(), tag, mod, opts]) + end +end diff --git a/mix.exs b/mix.exs index 8844a7e1..af1b34ff 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule DBConnection.Mixfile do use Mix.Project - @pools [:connection, :poolboy, :sojourn, :ownership] + @pools [:connection, :connection_pool, :poolboy, :sojourn, :ownership] @version "1.1.2" def project do