View Source PoolSup.Multi (pool_sup v0.6.2)

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

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.

Callback implementation for GenServer.init/1.

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

@type option() :: {:name, GenServer.name()} | {:checkout_max_duration, pos_integer()}
@type pool_multi() :: pid() | GenServer.name()
@type pool_multi_key() :: term()
@type 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
@spec 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
@spec 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

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

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
@spec 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
@spec 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.

@spec child_spec(list()) :: Supervisor.child_spec()

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

Callback implementation for GenServer.format_status/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

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
@spec 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.