pool_sup v0.5.0 PoolSup.Multi View Source
Defines a supervisor that is specialized to manage multiple PoolSup processes.
For high-throughput use cases, centralized process pool such as PoolSup
may become a bottleneck as all tasks must checkout from a single pool manager process.
This module is for these situations: to manage multiple PoolSups and
load-balance checkout requests to multiple pool manager processes.
In summary,
- Process defined by
PoolSup.Multibehaves as a:simple_one_for_onesupervisor. - Children of
PoolSup.MultiarePoolSupprocesses and they have identical configurations (worker module, capacity, etc.). checkout/3,checkout_nonblocking/3andtransaction/4which randomly picks a pool in aPoolSup.Multi(with the help of ETS) are provided.- Number of pools and capacity of each pool are dynamically configurable.
Example
Suppose we have the following worker module:
iex(1)> defmodule MyWorker do
...(1)> @behaviour PoolSup.Worker
...(1)> use GenServer
...(1)> def start_link(arg) do
...(1)> GenServer.start_link(__MODULE__, arg)
...(1)> end
...(1)> # definitions of gen_server callbacks...
...(1)> end
To use PoolSup.Multi it’s necessary to setup an ETS table.
iex(2)> table_id = :ets.new(:arbitrary_table_name, [:set, :public, {:read_concurrency, true}])
Note that the PoolSup.Multi process must be able to write to the table.
The following creates a PoolSup.Multi process that has 3 PoolSups each of which manages 5 reserved and 2 ondemand workers.
iex(3)> {:ok, pool_multi_pid} = PoolSup.Multi.start_link(table_id, "arbitrary_key", 3, MyWorker, {:worker, :arg}, 5, 2)
Now we can checkout a worker pid from the set of pools:
iex(4)> {pool_pid, worker_pid} = PoolSup.Multi.checkout(table_id, "arbitrary_key")
iex(5)> do_something(worker_pid)
iex(6)> PoolSup.checkin(pool_pid, worker_pid)
Link to this section Summary
Functions
Changes :checkout_max_duration option of child pools
Changes configuration of an existing PoolSup.Multi process
Checks out a worker pid that is currently not used
Checks out a worker pid in a nonblocking manner, i.e. if no available worker found in the randomly chosen pool this returns nil
Returns a child specification to be used when it’s not fully specified by the parent supervisor
Invoked in some cases to retrieve a formatted version of the GenServer status
Invoked when the server is started. start_link/3 or start/3 will
block until it returns
Starts a PoolSup.Multi process linked to the calling process
Picks a pool from the specified ETS record, checks out a worker pid, creates a link to the worker, executes the given function using the pid, and finally checks-in and unlink
Link to this section Types
option() :: {:name, GenServer.name()} | {:checkout_max_duration, pos_integer()}
pool_sup_args() :: [module() | term() | non_neg_integer() | non_neg_integer()]
Link to this section Functions
change_checkout_max_duration(pool_multi(), nil | pos_integer()) :: :ok
Changes :checkout_max_duration option of child pools.
See PoolSup.start_link/5 for detailed explanation of :checkout_max_duration option.
The change will be broadcasted to all existing pools.
Also all pools that start afterward will use the new value of :checkout_max_duration.
change_configuration(pool_multi(), nil_or_nni, nil_or_nni, nil_or_nni) :: :ok when nil_or_nni: nil | non_neg_integer()
Changes configuration of an existing PoolSup.Multi process.
new_n_pools, new_reserved and/or new_ondemand parameters can be nil; in that case the original value is kept unchanged.
Changing number of pools
- When
new_n_poolsis larger than the current number of working pools,PoolSup.Multispawns new pools immediately. When
new_n_poolsis smaller than the current number of working pools,PoolSup.Multiprocess- randomly chooses pools to terminate and mark them “not working”,
- exclude those pools from the ETS record,
- resets their
reservedandondemandas0so that new checkouts will never succeed, - starts to periodically poll the status of “not working” pools, and
- terminate a pool when it becomes ready to terminate (i.e. no worker process is used).
Changing reserved and/or ondemand of each pool
- The given values of
reserved,ondemandare notified to all the working pools. SeePoolSup.change_capacity/3for the behaviour of each pool.
checkout(:ets.tab(), pool_multi_key(), timeout()) :: {pid(), pid()}
Checks out a worker pid that is currently not used.
Internally this function looks-up the specified ETS record, randomly chooses one of the pools and checks-out a worker in the pool.
Note that this function returns a pair of pids: {pool_pid, worker_pid}.
The returned pool_pid must be used when returning the worker to the pool: PoolSup.checkin(pool_pid, worker_pid).
checkout_nonblocking(:ets.tab(), pool_multi_key(), timeout()) :: nil | {pid(), pid()}
Checks out a worker pid in a nonblocking manner, i.e. if no available worker found in the randomly chosen pool this returns nil.
Returns a child specification to be used when it’s not fully specified by the parent supervisor.
Invoked in some cases to retrieve a formatted version of the GenServer status.
This callback can be useful to control the appearance of the status of the
GenServer. For example, it can be used to return a compact representation of
the GenServer’s state to avoid having large state terms printed.
one of
:sys.get_status/1or:sys.get_status/2is invoked to get the status of theGenServer; in such cases,reasonis:normalthe
GenServerterminates abnormally and logs an error; in such cases,reasonis:terminate
pdict_and_state is a two-elements list [pdict, state] where pdict is a
list of {key, value} tuples representing the current process dictionary of
the GenServer and state is the current state of the GenServer.
Callback implementation for GenServer.format_status/2.
Invoked when the server is started. start_link/3 or start/3 will
block until it returns.
args is the argument term (second argument) passed to start_link/3.
Returning {:ok, state} will cause start_link/3 to return
{:ok, pid} and the process to enter its loop.
Returning {:ok, state, timeout} is similar to {:ok, state}
except handle_info(:timeout, state) will be called after timeout
milliseconds if no messages are received within the timeout.
Returning {:ok, state, :hibernate} is similar to
{:ok, state} except the process is hibernated before entering the loop. See
c:handle_call/3 for more information on hibernation.
Returning :ignore will cause start_link/3 to return :ignore and the
process will exit normally without entering the loop or calling c:terminate/2.
If used when part of a supervision tree the parent supervisor will not fail
to start nor immediately try to restart the GenServer. The remainder of the
supervision tree will be (re)started and so the GenServer should not be
required by other processes. It can be started later with
Supervisor.restart_child/2 as the child specification is saved in the parent
supervisor. The main use cases for this are:
- The
GenServeris disabled by configuration but might be enabled later. - An error occurred and it will be handled by a different mechanism than the
Supervisor. Likely this approach involves callingSupervisor.restart_child/2after a delay to attempt a restart.
Returning {:stop, reason} will cause start_link/3 to return
{:error, reason} and the process to exit with reason reason without
entering the loop or calling c:terminate/2.
Callback implementation for GenServer.init/1.
start_link( :ets.tab(), pool_multi_key(), non_neg_integer(), module(), term(), non_neg_integer(), non_neg_integer(), [option()] ) :: GenServer.on_start()
Starts a PoolSup.Multi process linked to the calling process.
Arguments
table_id: ID of the ETS table to use.pool_multi_key: Key to identify the record in the ETS table. Note thatPoolSup.Multikeeps track of thePoolSups within a single ETS record. Thus multiple instances ofPoolSup.Multican share the same ETS table (as long as they use unique keys).n_pools: Number of pools.worker_module: Callback module ofPoolSup.Worker.worker_init_arg: Value passed toworker_module.start_link/1.reserved: Number of reserved workers in eachPoolSup.ondemand: Number of ondemand workers in eachPoolSup.options: Keyword list of the following options::name: Used for name registration ofPoolSup.Multiprocess.:checkout_max_duration: An option passed to child pools. SeePoolSup.start_link/5for detail.
transaction(:ets.tab(), pool_multi_key(), (pid() -> a), timeout()) :: a when a: term()
Picks a pool from the specified ETS record, checks out a worker pid, creates a link to the worker, executes the given function using the pid, and finally checks-in and unlink.
The timeout parameter is used only in the checkout step; time elapsed during other steps are not counted.