Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions integration_test/cases/queue_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,29 @@ defmodule QueueTest do
assert P.run(pool, fn _ -> :result end) == :result
end

test "does not checkout a connection after its deadline" do
stack = [
{:ok, :state},
:ok,
fn opts ->
send(opts[:parent], :reconnected)
{:ok, :state}
end
]

{:ok, agent} = A.start_link(stack)

opts = [agent: agent, parent: self()]
{:ok, pool} = P.start_link(opts)
deadline = System.monotonic_time(:millisecond) - 1

assert_raise DBConnection.ConnectionError, ~r/deadline reached/, fn ->
P.run(pool, fn _ -> flunk("checkout succeeded") end, deadline: deadline)
end

assert_receive :reconnected
end

test "queue many async exits" do
stack = [{:ok, :state}] ++ List.duplicate({:idle, :state}, 20)
{:ok, agent} = A.start_link(stack)
Expand Down
2 changes: 1 addition & 1 deletion integration_test/ownership/owner_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ defmodule OwnerTest do
fn _ ->
assert_receive :reconnected
end,
timeout: 0
timeout: 10
)
end) =~ ~r"timed out"

Expand Down
36 changes: 25 additions & 11 deletions lib/db_connection/holder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ defmodule DBConnection.Holder do
pool_ref =
pool_ref(pool: pool, reference: ref, deadline: deadline, holder: holder, lock: lock)

checkout_result(holder, pool_ref, checkin_time)
checkout_result(holder, pool_ref, checkin_time, timeout)

{^lock, reply} ->
Process.demonitor(lock, [:flush])
Expand All @@ -332,20 +332,34 @@ defmodule DBConnection.Holder do
end
end

defp checkout_result(holder, pool_ref, checkin_time) do
try do
:ets.lookup(holder, :conn)
rescue
ArgumentError ->
# Deadline could hit and be handled pool before using connection
msg = "connection not available because deadline reached while in queue"
{:error, DBConnection.ConnectionError.exception(msg)}
defp checkout_result(holder, pool_ref, checkin_time, timeout) do
if deadline_reached?(timeout) do
err = checkout_deadline_error()
disconnect(pool_ref, err)
{:error, err}
else
[conn(module: mod, state: state)] ->
{:ok, pool_ref, mod, checkin_time, state}
try do
:ets.lookup(holder, :conn)
rescue
ArgumentError ->
# Deadline could hit and be handled by the pool before using the connection.
{:error, checkout_deadline_error()}
else
[conn(module: mod, state: state)] ->
{:ok, pool_ref, mod, checkin_time, state}
end
end
end

defp checkout_deadline_error do
DBConnection.ConnectionError.exception(
"connection not available because deadline reached while in queue"
)
end

defp deadline_reached?(nil), do: false
defp deadline_reached?(timeout), do: timeout <= System.monotonic_time(@time_unit)

defp no_holder(holder, maybe_pid) do
reason =
case :ets.info(holder, :owner) do
Expand Down
Loading