pool_sup v0.1.0 PoolSup

This module defines a supervisor process that is specialized to manage pool of workers.

  • Process defined by this module behaves as a :simple_one_for_one supervisor.
  • Worker processes are spawned using a callback module that implements PoolSup.Worker behaviour.
  • The PoolSup process manages which child processes are in use and which are not.
  • Functions to request pid of a child process that is not in use are also defined.

Example

Suppose we have a module that implements both GenServer and PoolSup.Worker behaviours.

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

When we want to have 3 processes that run MyWorker server:

iex(2)> {:ok, pid} = PoolSup.start_link(MyWorker, {:worker, :arg}, 3, [name: :my_pool])

Each child process is started by MyWorker.start_link({:worker, :arg}). Then we can get a pid of a child currently not in use.

iex(3)> child_pid = PoolSup.checkout(:my_pool)
iex(4)> do_something(child_pid)
iex(5)> PoolSup.checkin(:my_pool, child_pid)

Don’t forget to return the child_pid when finished; for simple use cases PoolSup.transaction/3 comes in handy.

Usage within supervision tree

The following code snippet spawns a supervisor that has PoolSup process as one of its children. The PoolSup process manages 5 worker processes and they will be started by MyWorker.start_link({:worker, :arg}).

chilldren = [
  ...
  Supervisor.Spec.supervisor(PoolSup, [MyWorker, {:worker, :arg}, 5]),
  ...
]
Supervisor.start_link(children, [strategy: :one_for_one])

Summary

Functions

Changes capacity (number of worker processes) of a pool

Checks in an in-use worker process and make it available to others

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 this returns nil

Starts a PoolSup process linked to the calling process

Query current status of a pool

Temporarily checks out a worker pid, executes the given function using the pid, and checks in the pid

Types

options :: [{:name, GenServer.name}]
pool :: pid | GenServer.name

Functions

change_capacity(pool, new_capacity)

Specs

change_capacity(pool, non_neg_integer) :: :ok

Changes capacity (number of worker processes) of a pool.

If new_capacity is more than the current capacity, new processes are immediately spawned and become available. Note that, as is the same throughout the OTP framework, spawning processes under supervisor is synchronous operation. Therefore increasing large number of capacity at once may make a pool unresponsive for a while.

If new_capacity is less than the current capacity, the pool tries to shutdown workers that are not in use. Processes currently in use are never interrupted. If number of in-use workers is more than new_capacity, reducing further is delayed until any worker process is checked in.

checkin(pool, pid)

Specs

checkin(pool, pid) :: :ok

Checks in an in-use worker process and make it available to others.

checkout(pool, timeout \\ 5000)

Specs

checkout(pool, timeout) :: nil | pid

Checks out a worker pid that is currently not used.

If no available worker process exists, the caller is blocked until either

  • any process becomes available, or
  • timeout is reached.
checkout_nonblock(pool, timeout \\ 5000)

Specs

checkout_nonblock(pool, timeout) :: nil | pid

Checks out a worker pid in a nonblocking manner, i.e. if no available worker found this returns nil.

start_link(worker_module, worker_init_arg, capacity, options \\ [])

Specs

start_link(module, term, non_neg_integer, options) :: GenServer.on_start

Starts a PoolSup process linked to the calling process.

Arguments

  • worker_module is the callback module of PoolSup.Worker.
  • worker_init_arg is the value passed to worker_module.start_link/1 callback function.
  • capacity is the initial number of workers this PoolSup process holds.
  • Currently only :name option is supported for name registration.
status(pool)

Specs

status(pool) :: %{current_capacity: nni, desired_capacity: nni, available: nni, working: nni} when nni: non_neg_integer

Query current status of a pool.

transaction(pool, f, timeout \\ 5000)

Specs

transaction(pool, (pid -> a), timeout) :: a when a: any

Temporarily checks out a worker pid, executes the given function using the pid, and checks in the pid.

The timeout parameter is used only in the checkout step; time elapsed during other steps are not counted.