Poolboy is a lightweight, generic pooling library for Erlang with a focus on simplicity, performance, and rock-solid disaster recovery.
1> Worker = poolboy:checkout(PoolName).
<0.9001.0>
2> gen_server:call(Worker, Request).
ok
3> poolboy:checkin(PoolName, Worker).
ok
This is an example application showcasing database connection pools using Poolboy and epgsql.
{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"}
]}
]}
]}
]}.-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).-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}.name: the pool nameworker_module: the module that represents the workerssize: maximum pool sizemax_overflow: maximum number of workers created if pool is emptyoverflow_ttl: idle time in milliseconds before excess workers may be removed. Defaults to0, which removes excess workers immediately on check-in when nobody is waiting. A positive value retains idle workers for reuse; checked-out workers are never reaped, and the pool never shrinks belowsize.overflow_check_period: interval in milliseconds between idle-worker checks. A value of0selects the default interval. Defaults to the smaller ofoverflow_ttland 1000 milliseconds when TTL is enabled. TTL is the minimum idle age for reaping, not an upper bound on retention. Checks and shutdowns may be delayed by scheduling or an earlier batch of slow-closing workers. The timer runs only while there are idle workers and excess capacity to reap, and pauses while a shutdown batch is in progress. This option has no effect whenoverflow_ttlis0. Expired workers are closed by a background process so slow shutdowns do not block borrowing an available worker. Closing workers still count towardmax_overflowuntil they have terminated. While the supervisor is closing a worker, creation and replacement are deferred. Blocking checkouts can still receive returned workers. If none are available, non-blocking checkouts returnfulland pool status reportsfull, even if there is spare overflow capacity.strategy:lifoorfifo, determines whether checked in workers should be placed first or last in the line of available workers. So,lifooperates like a traditional stack;fifolike a queue. Default islifo.
- Devin Torres (devinus) devin@devintorres.com
- Andrew Thompson (Vagabond) andrew@hijacked.us
- Kurt Williams (onkel-dirtus) kurt.r.williams@gmail.com
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.
