Erlang round-robin load balancer for Erlang processes based on ETS
Erlpool is a round-robin load balancer for Erlang processes meant mainly to be used with things like database connections.
Compared to pooler and poolboy, erlpool
is very simple and small (~100 LOC) with no assumptions about the workers.
Each time you ask for a pid the library is doing over ETS one counter_update
operation and one lookup
. This is inspired
from cuesport which beside this is doing an additional lookup
. This additional lookup
in erlpool
was transformed using
dynamic compilation in a simple function call. Also another project from where I got inspired is revolver.
Getting all deps and compile:
rebar3 compile
Let's suppose you have a gen_server
similar with the benchmark_worker
from benchmark folder. You create a pool for this in the following way:
application:ensure_all_started(erlpool),
Args = [
{size, 20},
{start_mfa, {benchmark_worker, start_link, [WorkerArgs]}},
{supervisor_period, 1},
{supervisor_intensity, 1000},
{supervisor_shutdown, 5000}
],
erlpool:start_pool(pool_name, Args).
Or in case you want to use the sys.config
you can use:
[
{erlpool, [
{pools, [
{mypool, [
{size, 50},
{group, mygroup},
{start_mfa, {benchmark_worker, start_link, [ [] ]} },
{supervisor_period, 1},
{supervisor_intensity, 1000},
{supervisor_restart, permanent},
{supervisor_shutdown, 5000}
]}
]}
]}
].
Arguments:
size
: the pool size (how many workers are created and added in the supervisor)group
: used to group multiple pools in a group. For example, you have two different apps that are using erlpool and you want to restart/delete the pools for app1. Using groups you can delete/restart all pools for a certain application.smart_mfa
: Defines the function call used to start the child process. It must be a module-function-arguments tuple{M,F,A}
used asapply(M,F,A)
args_include_worker_id
: Include the worker id into the arguments fromsmart_mfa
as first argument.supervisor_period
: the supervisor restart period in seconds (default to 1)supervisor_intensity
: the supervisor restart intensity (defaults to 100)supervisor_restart
: the supervisor restart strategy (permanent (default) | transient | temporary)supervisor_shutdown
: defines how the worker process must be terminated (brutal_kill | timeout()) - default 2000.
If you are not familiar with supervisor settings check the documentation. Basically to prevent a supervisor from getting
into an infinite loop of child process terminations and restarts, a maximum restart intensity is defined using two integer values.
Assuming the values supervisor_intensity
and supervisor_period
, then, if more than supervisor_intensity
restarts occur within
supervisor_period
seconds, the supervisor terminates all child processes and then itself. The intensity defaults to 100 and period defaults to 1.
Args = [
{size, 20},
{start_mfa, {benchmark_worker, start_link, [WorkerArgs]}},
{supervisor_period, 1},
{supervisor_intensity, 1000}
],
ok = erlpool:start_pool(pool_name, Args).
ok = erlpool:stop_pool(pool_name).
Use the following function when you want to restart a pool
ok = erlpool:restart_pool(pool_name).
To get a pid from a pool in a round robbin fashion you are using:
Pid = erlpool:pid(pool_name).
In case you want to run a function over all pid's in the pool you can use the map/2
function. For example the following
function returns all pid's in a list:
erlpool:map(pool_name, fun(Pid) -> Pid end).
To remove all pools in a group use:
erlpool:stop_group(group_name).
To restart all pools in a group use:
erlpool:restart_group(group_name).
The code is in benchmark
folder. The test sends 100000 requests from 4000 concurrent processes to a gen_server that
replies with ok. The pools have 20 workers.
make bench
### erlpool 231 ms 500000 req/sec
### cuesport 664 ms 166666 req/sec
### revolver 755 ms 142857 req/sec
### poolboy 1246 ms 83333 req/sec
### pooler 2587 ms 40000 req/sec
You can run it yourself using make bench
make ct