Skip to content

Commit 58acb4b

Browse files
committed
Spread both max_lifetime and disconnect_all over given intervals
1 parent 90011ff commit 58acb4b

3 files changed

Lines changed: 21 additions & 31 deletions

File tree

integration_test/connection_pool/disconnect_all_test.exs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,11 @@ defmodule TestPoolDisconnectAll do
5959

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

62-
opts = [agent: agent, parent: self(), idle_interval: 50]
62+
opts = [agent: agent, parent: self(), idle_interval: 200]
6363
{:ok, pool} = P.start_link(opts)
6464
assert P.execute(pool, %Q{}, [:param]) == {:ok, %Q{}, %R{}}
6565

66-
# High intervals do not affect ping because those are always disconnected.
67-
P.disconnect_all(pool, 45_000)
66+
P.disconnect_all(pool, 0)
6867
assert_receive :disconnecting
6968
assert P.execute(pool, %Q{}, [:param]) == {:ok, %Q{}, %R{}}
7069
assert P.execute(pool, %Q{}, [:param]) == {:ok, %Q{}, %R{}}

lib/db_connection/connection_pool.ex

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ defmodule DBConnection.ConnectionPool do
66
77
You're not supposed to call any functions on this pool directly, but only pass this
88
as the value of the `:pool` option in functions such as `DBConnection.start_link/2`.
9-
10-
`disconnect_all/3`, which by default will result in connections being
11-
reestablished, can be called periodically to recycle checked-in connections
12-
after a maximum lifetime is reached. `Ecto SQL` users may find it at
13-
https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.html#disconnect_all/3
149
"""
1510

1611
use GenServer
@@ -68,7 +63,7 @@ defmodule DBConnection.ConnectionPool do
6863
nil
6964
end
7065

71-
ts = {System.monotonic_time(), 0, max_lifetime}
66+
ts = {nil, max_lifetime}
7267
{:ok, _} = DBConnection.ConnectionPool.Pool.start_supervised(queue, mod, opts)
7368
target = Keyword.get(opts, :queue_target, @queue_target)
7469
interval = Keyword.get(opts, :queue_interval, @queue_interval)
@@ -114,8 +109,8 @@ defmodule DBConnection.ConnectionPool do
114109
end
115110

116111
def handle_call({:disconnect_all, interval}, _from, {type, queue, codel, ts}) do
117-
{_, _, max_lifetime} = ts
118-
ts = {System.monotonic_time(), interval, max_lifetime}
112+
{_, max_lifetime} = ts
113+
ts = {{System.monotonic_time(), interval}, max_lifetime}
119114
{:reply, :ok, {type, queue, codel, ts}}
120115
end
121116

@@ -165,9 +160,9 @@ defmodule DBConnection.ConnectionPool do
165160

166161
case :ets.info(holder, :owner) do
167162
^owner ->
168-
{time, interval, max_lifetime} = ts
163+
{interval, max_lifetime} = ts
169164

170-
if Holder.maybe_disconnect(holder, time, interval, max_lifetime) do
165+
if Holder.maybe_disconnect(holder, interval, max_lifetime) do
171166
{:noreply, data}
172167
else
173168
handle_checkin(holder, extra, data)
@@ -239,8 +234,8 @@ defmodule DBConnection.ConnectionPool do
239234
{queued_in_native, holder} = key when queued_in_native <= past_in_native <-
240235
:ets.first(queue) do
241236
:ets.delete(queue, key)
242-
{time, _interval, max_lifetime} = ts
243-
Holder.maybe_disconnect(holder, time, 0, max_lifetime) or Holder.handle_ping(holder)
237+
{interval, max_lifetime} = ts
238+
Holder.maybe_disconnect(holder, interval, max_lifetime) or Holder.handle_ping(holder)
244239
drop_idle(past_in_native, limit - 1, status, queue, codel, ts)
245240
else
246241
_ ->

lib/db_connection/holder.ex

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,13 @@ defmodule DBConnection.Holder do
252252
handle_done(holder, &DBConnection.Connection.stop/3, err)
253253
end
254254

255-
@spec maybe_disconnect(t, integer, non_neg_integer, {integer, non_neg_integer} | nil) ::
255+
@spec maybe_disconnect(t, {integer, non_neg_integer} | nil, {integer, non_neg_integer} | nil) ::
256256
boolean()
257-
def maybe_disconnect(holder, start, interval_ms, lifetime) do
258-
ts = :ets.lookup_element(holder, :conn, conn(:connected_at) + 1)
257+
def maybe_disconnect(_holder, nil, nil), do: false
259258

260-
disconnect_all_reason(start, interval_ms, ts, holder) ||
261-
max_lifetime_reason(lifetime, ts, holder)
259+
def maybe_disconnect(holder, interval, lifetime) do
260+
ts = :ets.lookup_element(holder, :conn, conn(:connected_at) + 1)
261+
disconnect_all_reason(holder, ts, interval) || max_lifetime_reason(holder, ts, lifetime)
262262
rescue
263263
_ -> false
264264
else
@@ -270,26 +270,22 @@ defmodule DBConnection.Holder do
270270
handle_disconnect(holder, DBConnection.ConnectionError.exception(opts))
271271
end
272272

273-
defp max_lifetime_reason(nil, _ts, _holder), do: nil
273+
defp max_lifetime_reason(_holder, _ts, nil), do: nil
274274

275-
defp max_lifetime_reason({start, interval_ms}, ts, holder) do
275+
defp max_lifetime_reason(holder, ts, {min_lifetime, interval_ms}) do
276276
elapsed = System.monotonic_time() - ts
277277

278278
# First check if passed start then check if also the interval
279-
if elapsed > start and elapsed > hash_holder(holder, interval_ms) + start do
279+
if elapsed > min_lifetime and elapsed > hash_holder(holder, interval_ms) + min_lifetime do
280280
"max_lifetime exceeded"
281281
end
282282
end
283283

284-
defp disconnect_all_reason(start, interval_ms, ts, holder) do
285-
disconnect? =
286-
cond do
287-
ts >= start -> false
288-
interval_ms == 0 -> true
289-
true -> System.monotonic_time() > hash_holder(holder, interval_ms) + start
290-
end
284+
defp disconnect_all_reason(_holder, _ts, nil), do: nil
291285

292-
if disconnect? do
286+
defp disconnect_all_reason(holder, ts, {disconnect_start, interval_ms}) do
287+
if disconnect_start > ts and
288+
System.monotonic_time() > hash_holder(holder, interval_ms) + disconnect_start do
293289
"disconnect_all requested"
294290
end
295291
end

0 commit comments

Comments
 (0)