pool_sup v0.6.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,

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

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

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

Link to this type option() View Source
option() :: {:name, GenServer.name()} | {:checkout_max_duration, pos_integer()}
Link to this type pool_multi() View Source
pool_multi() :: pid() | GenServer.name()
Link to this type pool_multi_key() View Source
pool_multi_key() :: term()
Link to this type pool_sup_args() View Source
pool_sup_args() :: [module() | term() | non_neg_integer() | non_neg_integer()]

Link to this section Functions

Link to this function change_checkout_max_duration(pid_or_name, new_duration) View Source
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.

Link to this function change_configuration(pid_or_name, new_n_pools, new_reserved, new_ondemand) View Source
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_pools is larger than the current number of working pools, PoolSup.Multi spawns new pools immediately.
  • When new_n_pools is smaller than the current number of working pools, PoolSup.Multi process

    • randomly chooses pools to terminate and mark them “not working”,
    • exclude those pools from the ETS record,
    • resets their reserved and ondemand as 0 so 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, ondemand are notified to all the working pools. See PoolSup.change_capacity/3 for the behaviour of each pool.
Link to this function checkout(table_id, pool_multi_key, timeout \\ 5000) View Source
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).

Link to this function checkout_nonblocking(table_id, pool_multi_key, timeout \\ 5000) View Source
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.

Link to this function format_status(opt, list) View Source

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.

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 GenServer is 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 calling Supervisor.restart_child/2 after 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.

Link to this function start_link(table_id, pool_multi_key, n_pools, worker_module, worker_init_arg, reserved, ondemand, options \\ []) View Source

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 that PoolSup.Multi keeps track of the PoolSups within a single ETS record. Thus multiple instances of PoolSup.Multi can share the same ETS table (as long as they use unique keys).
  • n_pools: Number of pools.
  • worker_module: Callback module of PoolSup.Worker.
  • worker_init_arg: Value passed to worker_module.start_link/1.
  • reserved: Number of reserved workers in each PoolSup.
  • ondemand: Number of ondemand workers in each PoolSup.
  • options: Keyword list of the following options:

Link to this function transaction(table_id, pool_multi_key, f, timeout \\ 5000) View Source
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.