Poolgirl is a lightweight, generic pooling library for Erlang with a focus on simplicity, performance, and rock-solid disaster recovery. Poolgirl is Poolboy's little sister, it's directed towards the use-case of simple workers that process requests with no need for a reply (ie. that only handle cast requests)
1> Worker = poolgirl:checkout(PoolName).
<0.9001.0>
2> gen_server:call(Worker, Request).
ok
Q: Is this in any way related with poolboy? A: Yes, the interface and configuration is mostly the same, however poolgirl adds some additional methods.
Q: So what's the difference, why another worker pool manager? A: Poolboy's manager of workers is a single gen_server process, in pools with a large number of workers the manager itself can become a bottleneck, a possible solution is to have multiple managers and load balance these. In use-case scenarios where the pool workers only handle cast requests and have a fast execution time per request (eg. analytics reporting, stats reporting) poolgirl overcomes this issue and lets you have high worker numbers with no additional overhead.
Q: How does it work under the hood? A: When a client requests a worker from the pool no messages are sent to any other process in order to accomplish this, poolgirl accesses a pg2 process group and recruits a random worker.
Q: But won't that overload the workers message queue? You're basically choosing a worker at random. A: That is true, hence the fact the poolgirl is best suited to the use-case described above, if you're making long synchronous requests to workers then you're probably better off sticking with poolboy.
Q: So if it's the client that recruits the worker what does the checkin operation do? A: Nothing, it's there basically to ease the transition between poolboy <-> poolgirl
checkout/1
: recruits a worker from the requested poolcheckin/1
: does nothingtransaction/2
: recruits a worker from the requested pool and executes a funcion with the worker pid as argumentget_workers/1
: returns a list of worker pidsstatus/1
: returns the pool statusspin/3
: spins up/down pool workers, these are immediately added/removed from the worker pool
This is an example application showcasing database connection pools using Poolgirl 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}
], [
{hostname, "127.0.0.1"},
{database, "db1"},
{username, "db1"},
{password, "abc123"}
]},
{pool2, [
{size, 5}
], [
{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,
poolgirl:child_spec(Name, PoolArgs, WorkerArgs)
end, Pools),
{ok, {{one_for_one, 10, 10}, PoolSpecs}}.
squery(PoolName, Sql) ->
poolgirl:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {squery, Sql})
end).
equery(PoolName, Stmt, Params) ->
poolgirl:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {equery, Stmt, Params})
end).
-module(example_worker).
-behaviour(gen_server).
-behaviour(poolgirl_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 size
- Luis Rascão (lrascao) [email protected]
Poolgirl is available under the MIT license (see LICENSE).