Skip to content

Commit d848389

Browse files
committed
Allow unallow_existing as an opt to ownership_allow/4
1 parent 2b20ce7 commit d848389

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

integration_test/ownership/manager_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ defmodule ManagerTest do
4949

5050
test "connection may be shared with other processes" do
5151
{:ok, pool, _opts} = start_pool()
52+
{:ok, pool_2, _opts} = start_pool()
5253
parent = self()
5354

5455
Task.await(
@@ -63,6 +64,12 @@ defmodule ManagerTest do
6364
assert Ownership.ownership_allow(pool, self(), self(), []) ==
6465
{:already, :owner}
6566

67+
:ok = Ownership.ownership_checkout(pool_2, [])
68+
69+
assert Ownership.ownership_allow(pool_2, self(), self(), unallow_existing: true) == :ok
70+
71+
assert Ownership.ownership_allow(pool_2, self(), self(), []) == {:already, :allowed}
72+
6673
Task.await(
6774
async_no_callers(fn ->
6875
assert Ownership.ownership_allow(pool, parent, self(), []) ==
@@ -73,6 +80,13 @@ defmodule ManagerTest do
7380

7481
assert Ownership.ownership_checkin(pool, []) == :not_owner
7582

83+
# Test that we can switch our 'allow' to another pool and back again
84+
assert Ownership.ownership_allow(pool_2, parent, self(), unallow_existing: true) == :ok
85+
86+
assert Ownership.ownership_allow(pool_2, parent, self(), []) == {:already, :allowed}
87+
88+
assert Ownership.ownership_allow(pool, parent, self(), unallow_existing: true) == :ok
89+
7690
parent = self()
7791

7892
Task.await(

lib/db_connection/ownership.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ defmodule DBConnection.Ownership do
144144
has a connection. `owner_or_allowed` may either be the owner or any
145145
other allowed process. Returns `:not_found` if the given process
146146
does not have any connection checked out.
147+
148+
Setting the `unallow_existing` option to `true` will remove the process given by `allow` from
149+
any existing allowance it may have (this is necessary because a given process can only be
150+
allowed on a single connection at a time).
147151
"""
148152
@spec ownership_allow(GenServer.server(), owner_or_allowed :: pid, allow :: pid, Keyword.t()) ::
149153
:ok | {:already, :owner | :allowed} | :not_found

lib/db_connection/ownership/manager.ex

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ defmodule DBConnection.Ownership.Manager do
5656
:ok | {:already, :owner | :allowed} | :not_found
5757
def allow(manager, parent, allow, opts) do
5858
timeout = Keyword.get(opts, :timeout, @timeout)
59-
GenServer.call(manager, {:allow, parent, allow}, timeout)
59+
passed_opts = Keyword.take(opts, [:unallow_existing])
60+
GenServer.call(manager, {:allow, parent, allow, passed_opts}, timeout)
6061
end
6162

6263
@spec get_connection_metrics(GenServer.server()) ::
@@ -170,15 +171,24 @@ defmodule DBConnection.Ownership.Manager do
170171
{:reply, reply, state}
171172
end
172173

173-
def handle_call({:allow, caller, allow}, _from, %{checkouts: checkouts} = state) do
174-
if kind = already_checked_out(checkouts, allow) do
174+
def handle_call({:allow, caller, allow, opts}, _from, %{checkouts: checkouts} = state) do
175+
unallow_existing = Keyword.get(opts, :unallow_existing, false)
176+
kind = already_checked_out(checkouts, allow)
177+
178+
if !unallow_existing && kind do
175179
{:reply, {:already, kind}, state}
176180
else
177181
case Map.get(checkouts, caller, :not_found) do
178182
{:owner, ref, proxy} ->
183+
state =
184+
if unallow_existing, do: owner_unallow(state, caller, allow, ref, proxy), else: state
185+
179186
{:reply, :ok, owner_allow(state, caller, allow, ref, proxy)}
180187

181188
{:allowed, ref, proxy} ->
189+
state =
190+
if unallow_existing, do: owner_unallow(state, caller, allow, ref, proxy), else: state
191+
182192
{:reply, :ok, owner_allow(state, caller, allow, ref, proxy)}
183193

184194
:not_found ->
@@ -310,6 +320,24 @@ defmodule DBConnection.Ownership.Manager do
310320
state
311321
end
312322

323+
defp owner_unallow(%{ets: ets, log: log} = state, caller, unallow, ref, proxy) do
324+
if log do
325+
Logger.log(log, fn ->
326+
[inspect(unallow), " was unallowed by ", inspect(caller), " on proxy ", inspect(proxy)]
327+
end)
328+
end
329+
330+
state = update_in(state.checkouts, &Map.delete(&1, unallow))
331+
332+
state =
333+
update_in(state.owners[ref], fn {proxy, caller, allowed} ->
334+
{proxy, caller, List.delete(allowed, unallow)}
335+
end)
336+
337+
ets && :ets.delete(ets, {unallow, proxy})
338+
state
339+
end
340+
313341
defp owner_down(%{ets: ets, log: log} = state, ref) do
314342
case get_and_update_in(state.owners, &Map.pop(&1, ref)) do
315343
{{proxy, caller, allowed}, state} ->

0 commit comments

Comments
 (0)