Packages
db_connection
0.2.2
2.10.2
2.10.1
2.10.0
2.9.0
2.8.1
2.8.0
2.7.0
2.6.0
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.2
2.2.1
2.2.0
2.1.1
2.1.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Database connection behaviour for database transactions and connection pooling
Current section
Files
Jump to
Current section
Files
lib/db_connection/ownership.ex
defmodule DBConnection.Ownership do
@moduledoc """
A `DBConnection.Pool` that requires explicit checkout and checking
as a mechanism to coordinate between processes.
### Options
* `:ownership_pool` - The actual pool to use to power the ownership
mechanism. The pool is started when the ownership pool is started,
although this option may also be given on `ownership_checkout/2`
allowing developers to customize the pool checkout/checkin
* `:ownership_mode` - When mode is `:manual`, all connections must
be explicitly checked out before by using `ownership_checkout/2`.
Otherwise, mode is `:auto` and connections are checked out
implicitly. In both cases, checkins are implicit via
`ownership_checkin/2`. Defaults to `:auto`.
If the `:ownership_pool` has an atom name given in the `:name` option,
an ETS table will be created and automatically used for lookups whenever
the name is used on checkout.
Finally, if the `:caller` option is given on checkout with a pid and no
pool is assigned to the current process, a connection will be allowed
from the given pid and used on checkout with `:pool_timeout` of `:infinity`.
This is useful when multiple tasks need to collaborate on the same
connection (hence the `:infinity` timeout).
"""
@behaviour DBConnection.Pool
alias DBConnection.Ownership.Manager
alias DBConnection.Ownership.Owner
## Ownership API
@doc """
Explicitly checks a connection out from the ownership manager.
It may return `:ok` if the connection is checked out.
`{:already, :owner | :allowed}` if the caller process already
has a connection, `:error` if it could be not checked out or
raise if there was an error.
"""
@spec ownership_checkout(GenServer.server, Keyword.t) ::
:ok | {:already, :owner | :allowed} | :error | no_return
def ownership_checkout(manager, opts) do
case Manager.checkout(manager, opts) do
{:init, owner} -> Owner.init(owner, opts)
{:already, _} = already -> already
end
end
@doc """
Changes the ownwership mode.
`mode` may be `:auto` or `:manual`. Returns `:ok`.
"""
@spec ownership_mode(GenServer.server, :auto | :manual, Keyword.t) ::
:ok
defdelegate ownership_mode(manager, mode, opts), to: Manager, as: :mode
@doc """
Checks a connection back in.
A connection can only be checked back in by its owner.
"""
@spec ownership_checkin(GenServer.server, Keyword.t) ::
:ok | :not_owner | :not_found
defdelegate ownership_checkin(manager, opts), to: Manager, as: :checkin
@doc """
Allows the process given by `allow` to use the connection checked out by `owner`.
It may return `:ok` if the connection is checked out.
`{:already, :owner | :allowed}` if the `allow` process already
has a connection, `:not_owner` if the owner process is not an
owner or `:not_found` if the owner process does not have any
connection checked out.
"""
@spec ownership_allow(GenServer.server, owner :: pid, allow :: pid, Keyword.t) ::
:ok | {:already, :owner | :allowed} | :not_owner | :not_found
defdelegate ownership_allow(manager, owner, allow, opts), to: Manager, as: :allow
## Pool callbacks
@doc false
def start_link(module, opts) do
Manager.start_link(module, opts)
end
@doc false
def child_spec(module, opts, child_opts) do
Supervisor.Spec.worker(Manager, [module, opts], child_opts)
end
@doc false
def checkout(manager, opts) do
case Manager.lookup(manager, opts) do
{:init, owner} ->
case Owner.init(owner, opts) do
:ok -> Owner.checkout(owner, opts)
{:error, _} = error -> error
end
{:ok, owner} ->
Owner.checkout(owner, opts)
:not_found ->
case Keyword.pop(opts, :caller) do
{nil, _} ->
msg = "cannot find ownership process for #{inspect self()}. " <>
"This may happen if you have not explicitly checked out or " <>
"the checked out process crashed"
{:error, RuntimeError.exception(msg)}
{owner, opts} ->
ownership_allow(manager, owner, self(), opts)
checkout(manager, [pool_timeout: :infinity] ++ opts)
end
end
end
@doc false
def checkin(owner, state, opts) do
Owner.checkin(owner, state, opts)
end
@doc false
def disconnect(owner, exception, state, opts) do
Owner.disconnect(owner, exception, state, opts)
end
@doc false
def stop(owner, reason, state, opts) do
Owner.stop(owner, reason, state, opts)
end
end