Skip to content
Merged
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
17 changes: 13 additions & 4 deletions lib/db_connection/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule DBConnection.Connection do
require Logger
alias DBConnection.Backoff
alias DBConnection.Holder
alias DBConnection.Util

@timeout 15_000

Expand Down Expand Up @@ -47,6 +48,10 @@ defmodule DBConnection.Connection do
@doc false
@impl :gen_statem
def init({mod, opts, pool, tag}) do
pool_index = Keyword.get(opts, :pool_index)
label = if pool_index, do: "db_conn_#{pool_index}", else: "db_conn"
Util.set_label(label)

s = %{
mod: mod,
opts: opts,
Expand Down Expand Up @@ -267,15 +272,19 @@ defmodule DBConnection.Connection do
:no_state,
%{client: {ref, :after_connect}} = s
) do
message = "client #{inspect(pid)} exited: " <> Exception.format_exit(reason)
message =
"client #{Util.inspect_pid(pid)} exited: " <> Exception.format_exit(reason)

err = DBConnection.ConnectionError.exception(message)

{:keep_state, %{s | client: {nil, :after_connect}},
{:next_event, :internal, {:disconnect, {down_log(reason), err}}}}
end

def handle_event(:info, {:DOWN, mon, _, pid, reason}, :no_state, %{client: {ref, mon}} = s) do
message = "client #{inspect(pid)} exited: " <> Exception.format_exit(reason)
message =
"client #{Util.inspect_pid(pid)} exited: " <> Exception.format_exit(reason)

err = DBConnection.ConnectionError.exception(message)

{:keep_state, %{s | client: {ref, nil}},
Expand All @@ -290,14 +299,14 @@ defmodule DBConnection.Connection do
)
when is_reference(timer) do
message =
"client #{inspect(pid)} timed out because it checked out " <>
"client #{Util.inspect_pid(pid)} timed out because it checked out " <>
"the connection for longer than #{timeout}ms"

exc =
case Process.info(pid, :current_stacktrace) do
{:current_stacktrace, stacktrace} ->
message <>
"\n\n#{inspect(pid)} was at location:\n\n" <>
"\n\n#{Util.inspect_pid(pid)} was at location:\n\n" <>
Exception.format_stacktrace(stacktrace)

_ ->
Expand Down
7 changes: 4 additions & 3 deletions lib/db_connection/connection_pool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule DBConnection.ConnectionPool do

use GenServer
alias DBConnection.Holder
alias DBConnection.Util

@behaviour DBConnection.Pool

Expand Down Expand Up @@ -131,7 +132,7 @@ defmodule DBConnection.ConnectionPool do
end

def handle_info({:"ETS-TRANSFER", holder, pid, queue}, {_, queue, _, _} = data) do
message = "client #{inspect(pid)} exited"
message = "client #{Util.inspect_pid(pid)} exited"
err = DBConnection.ConnectionError.exception(message: message, severity: :info)
Holder.handle_disconnect(holder, err)
{:noreply, data}
Expand Down Expand Up @@ -170,14 +171,14 @@ defmodule DBConnection.ConnectionPool do
# Check that timeout refers to current holder (and not previous)
if Holder.handle_deadline(holder, deadline) do
message =
"client #{inspect(pid)} timed out because " <>
"client #{Util.inspect_pid(pid)} timed out because " <>
"it queued and checked out the connection for longer than #{len}ms"

exc =
case Process.info(pid, :current_stacktrace) do
{:current_stacktrace, stacktrace} ->
message <>
"\n\n#{inspect(pid)} was at location:\n\n" <>
"\n\n#{Util.inspect_pid(pid)} was at location:\n\n" <>
Exception.format_stacktrace(stacktrace)

_ ->
Expand Down
3 changes: 2 additions & 1 deletion lib/db_connection/ownership/manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule DBConnection.Ownership.Manager do
use GenServer
require Logger
alias DBConnection.Ownership.Proxy
alias DBConnection.Util

@timeout 5_000

Expand Down Expand Up @@ -366,7 +367,7 @@ defmodule DBConnection.Ownership.Manager do

defp not_found({pid, _} = from) do
msg = """
cannot find ownership process for #{inspect(pid)}.
cannot find ownership process for #{Util.inspect_pid(pid)}.

When using ownership, you must manage connections in one
of the four ways:
Expand Down
11 changes: 7 additions & 4 deletions lib/db_connection/ownership/proxy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule DBConnection.Ownership.Proxy do
@moduledoc false

alias DBConnection.Holder
alias DBConnection.Util
use GenServer, restart: :temporary

@time_unit 1000
Expand All @@ -21,6 +22,8 @@ defmodule DBConnection.Ownership.Proxy do

@impl true
def init({caller, pool, pool_opts}) do
Util.set_label("db_ownership_proxy")

pool_opts =
pool_opts
|> Keyword.put(:timeout, :infinity)
Expand Down Expand Up @@ -58,13 +61,13 @@ defmodule DBConnection.Ownership.Proxy do

@impl true
def handle_info({:DOWN, ref, _, pid, _reason}, %{owner: {_, ref}} = state) do
down("owner #{inspect(pid)} exited", state)
down("owner #{Util.inspect_pid(pid)} exited", state)
end

def handle_info({:timeout, deadline, {_ref, holder, pid, len}}, %{holder: holder} = state) do
if Holder.handle_deadline(holder, deadline) do
message =
"client #{inspect(pid)} timed out because " <>
"client #{Util.inspect_pid(pid)} timed out because " <>
"it queued and checked out the connection for longer than #{len}ms"

down(message, state)
Expand All @@ -78,7 +81,7 @@ defmodule DBConnection.Ownership.Proxy do
%{ownership_timer: timer} = state
) do
message =
"owner #{inspect(pid)} timed out because " <>
"owner #{Util.inspect_pid(pid)} timed out because " <>
"it owned the connection for longer than #{timeout}ms (set via the :ownership_timeout option)"

# We don't invoke down because this is always a disconnect, even if there is no client.
Expand Down Expand Up @@ -150,7 +153,7 @@ defmodule DBConnection.Ownership.Proxy do
end

def handle_info({:"ETS-TRANSFER", holder, pid, ref}, %{holder: holder, owner: {_, ref}} = state) do
down("client #{inspect(pid)} exited", state)
down("client #{Util.inspect_pid(pid)} exited", state)
end

@impl true
Expand Down
2 changes: 2 additions & 0 deletions lib/db_connection/task.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ defmodule DBConnection.Task do
end

def init(fun, parent, opts) do
DBConnection.Util.set_label("db_after_connect_task")

try do
Process.link(parent)
catch
Expand Down
52 changes: 52 additions & 0 deletions lib/db_connection/util.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule DBConnection.Util do
@moduledoc false

@doc """
Inspect a pid, including the process label if possible.
"""
def inspect_pid(pid) do
with :undefined <- get_label(pid),
:undefined <- get_name(pid) do
inspect(pid)
else
label_or_name -> "#{inspect(pid)} (#{inspect(label_or_name)})"
end
end

defp get_name(pid) do
try do
Process.info(pid, :registered_name)
rescue
_ -> :undefined
else
{:registered_name, name} when is_atom(name) -> name
_ -> :undefined
end
end

@doc """
Set a process label if `Process.set_label/1` is available.
"""
def set_label(label) do
if function_exported?(:proc_lib, :set_label, 1) do
:proc_lib.set_label(label)

Check warning on line 32 in lib/db_connection/util.ex

View workflow job for this annotation

GitHub Actions / test (1.14, 24.2)

:proc_lib.set_label/1 is undefined or private
else
:ok
end
end

# Get a process label if `:proc_lib.get_label/1` is available.
defp get_label(pid) do
if function_exported?(:proc_lib, :get_label, 1) do
# Avoid a compiler warning if the function isn't
# defined in your version of Erlang/OTP
apply(:proc_lib, :get_label, [pid])
else
# mimic return value of
# `:proc_lib.get_label/1` when none is set.
# Don't resort to using `Process.info(pid, :dictionary)`,
# as this is not efficient.
:undefined
end
end
end