Poolboy - A hunky Erlang worker pool factory
Poolboy is a lightweight, generic pooling library for Erlang with a focus on simplicity, performance, and rock-solid disaster recovery.
Usage
1> Worker = poolboy:checkout(PoolName).
<0.9001.0>
2> gen_server:call(Worker, Request).
ok
3> poolboy:checkin(PoolName, Worker).
ok
Example
This is an example application showcasing database connection pools using Poolboy and epgsql.
example.app
{application, example, [
{description, "An example application"},
{vsn, "0.1"},
{applications, [kernel, stdlib, sasl, crypto, ssl]},
{modules, [example, example_worker]},
{registered, [example]},
{mod, {example, []}},
{env, [
{pools, [
{pool1, [
{size, 10},
{max_overflow, 20}
], [
{hostname, "127.0.0.1"},
{database, "db1"},
{username, "db1"},
{password, "abc123"}
]},
{pool2, [
{size, 5},
{max_overflow, 10}
], [
{hostname, "127.0.0.1"},
{database, "db2"},
{username, "db2"},
{password, "abc123"}
]}
]}
]}
]}.
example.erl
-module(example).
-behaviour(application).
-behaviour(supervisor).
-export([start/0, stop/0, squery/2, equery/3]).
-export([start/2, stop/1]).
-export([init/1]).
start() ->
application:start(?MODULE).
stop() ->
application:stop(?MODULE).
start(_Type, _Args) ->
supervisor:start_link({local, example_sup}, ?MODULE, []).
stop(_State) ->
ok.
init([]) ->
{ok, Pools} = application:get_env(example, pools),
PoolSpecs = lists:map(fun({Name, SizeArgs, WorkerArgs}) ->
PoolArgs = [{name, {local, Name}},
{worker_module, example_worker}] ++ SizeArgs,
poolboy:child_spec(Name, PoolArgs, WorkerArgs)
end, Pools),
{ok, {{one_for_one, 10, 10}, PoolSpecs}}.
squery(PoolName, Sql) ->
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {squery, Sql})
end).
equery(PoolName, Stmt, Params) ->
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {equery, Stmt, Params})
end).
example_worker.erl
-module(example_worker).
-behaviour(gen_server).
-behaviour(poolboy_worker).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {conn}).
start_link(Args) ->
gen_server:start_link(?MODULE, Args, []).
init(Args) ->
process_flag(trap_exit, true),
Hostname = proplists:get_value(hostname, Args),
Database = proplists:get_value(database, Args),
Username = proplists:get_value(username, Args),
Password = proplists:get_value(password, Args),
{ok, Conn} = epgsql:connect(Hostname, Username, Password, [
{database, Database}
]),
{ok, #state{conn=Conn}}.
handle_call({squery, Sql}, _From, #state{conn=Conn}=State) ->
{reply, epgsql:squery(Conn, Sql), State};
handle_call({equery, Stmt, Params}, _From, #state{conn=Conn}=State) ->
{reply, epgsql:equery(Conn, Stmt, Params), State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #state{conn=Conn}) ->
ok = epgsql:close(Conn),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Options
name
: the pool nameworker_module
: the module that represents the workerssize
: maximum pool sizemax_overflow
: maximum number of workers created if pool is emptystrategy
:lifo
orfifo
, determines whether checked in workers should be placed first or last in the line of available workers. So,lifo
operates like a traditional stack;fifo
like a queue. Default islifo
.
Authors
- Devin Torres (devinus) devin@devintorres.com
- Andrew Thompson (Vagabond) andrew@hijacked.us
- Kurt Williams (onkel-dirtus) kurt.r.williams@gmail.com
License
Poolboy is available in the public domain (see UNLICENSE
).
Poolboy is also optionally available under the ISC license (see LICENSE
),
meant especially for jurisdictions that do not recognize public domain works.