6 |
7u83 |
1 |
# Poolboy - A hunky Erlang worker pool factory
|
|
|
2 |
|
|
|
3 |
[](https://travis-ci.org/devinus/poolboy)
|
|
|
4 |
|
|
|
5 |
[](https://gratipay.com/devinus/)
|
|
|
6 |
|
|
|
7 |
Poolboy is a **lightweight**, **generic** pooling library for Erlang with a
|
|
|
8 |
focus on **simplicity**, **performance**, and **rock-solid** disaster recovery.
|
|
|
9 |
|
|
|
10 |
## Usage
|
|
|
11 |
|
|
|
12 |
```erl-sh
|
|
|
13 |
1> Worker = poolboy:checkout(PoolName).
|
|
|
14 |
<0.9001.0>
|
|
|
15 |
2> gen_server:call(Worker, Request).
|
|
|
16 |
ok
|
|
|
17 |
3> poolboy:checkin(PoolName, Worker).
|
|
|
18 |
ok
|
|
|
19 |
```
|
|
|
20 |
|
|
|
21 |
## Example
|
|
|
22 |
|
|
|
23 |
This is an example application showcasing database connection pools using
|
|
|
24 |
Poolboy and [epgsql](https://github.com/epgsql/epgsql).
|
|
|
25 |
|
|
|
26 |
### example.app
|
|
|
27 |
|
|
|
28 |
```erlang
|
|
|
29 |
{application, example, [
|
|
|
30 |
{description, "An example application"},
|
|
|
31 |
{vsn, "0.1"},
|
|
|
32 |
{applications, [kernel, stdlib, sasl, crypto, ssl]},
|
|
|
33 |
{modules, [example, example_worker]},
|
|
|
34 |
{registered, [example]},
|
|
|
35 |
{mod, {example, []}},
|
|
|
36 |
{env, [
|
|
|
37 |
{pools, [
|
|
|
38 |
{pool1, [
|
|
|
39 |
{size, 10},
|
|
|
40 |
{max_overflow, 20}
|
|
|
41 |
], [
|
|
|
42 |
{hostname, "127.0.0.1"},
|
|
|
43 |
{database, "db1"},
|
|
|
44 |
{username, "db1"},
|
|
|
45 |
{password, "abc123"}
|
|
|
46 |
]},
|
|
|
47 |
{pool2, [
|
|
|
48 |
{size, 5},
|
|
|
49 |
{max_overflow, 10}
|
|
|
50 |
], [
|
|
|
51 |
{hostname, "127.0.0.1"},
|
|
|
52 |
{database, "db2"},
|
|
|
53 |
{username, "db2"},
|
|
|
54 |
{password, "abc123"}
|
|
|
55 |
]}
|
|
|
56 |
]}
|
|
|
57 |
]}
|
|
|
58 |
]}.
|
|
|
59 |
```
|
|
|
60 |
|
|
|
61 |
### example.erl
|
|
|
62 |
|
|
|
63 |
```erlang
|
|
|
64 |
-module(example).
|
|
|
65 |
-behaviour(application).
|
|
|
66 |
-behaviour(supervisor).
|
|
|
67 |
|
|
|
68 |
-export([start/0, stop/0, squery/2, equery/3]).
|
|
|
69 |
-export([start/2, stop/1]).
|
|
|
70 |
-export([init/1]).
|
|
|
71 |
|
|
|
72 |
start() ->
|
|
|
73 |
application:start(?MODULE).
|
|
|
74 |
|
|
|
75 |
stop() ->
|
|
|
76 |
application:stop(?MODULE).
|
|
|
77 |
|
|
|
78 |
start(_Type, _Args) ->
|
|
|
79 |
supervisor:start_link({local, example_sup}, ?MODULE, []).
|
|
|
80 |
|
|
|
81 |
stop(_State) ->
|
|
|
82 |
ok.
|
|
|
83 |
|
|
|
84 |
init([]) ->
|
|
|
85 |
{ok, Pools} = application:get_env(example, pools),
|
|
|
86 |
PoolSpecs = lists:map(fun({Name, SizeArgs, WorkerArgs}) ->
|
|
|
87 |
PoolArgs = [{name, {local, Name}},
|
|
|
88 |
{worker_module, example_worker}] ++ SizeArgs,
|
|
|
89 |
poolboy:child_spec(Name, PoolArgs, WorkerArgs)
|
|
|
90 |
end, Pools),
|
|
|
91 |
{ok, {{one_for_one, 10, 10}, PoolSpecs}}.
|
|
|
92 |
|
|
|
93 |
squery(PoolName, Sql) ->
|
|
|
94 |
poolboy:transaction(PoolName, fun(Worker) ->
|
|
|
95 |
gen_server:call(Worker, {squery, Sql})
|
|
|
96 |
end).
|
|
|
97 |
|
|
|
98 |
equery(PoolName, Stmt, Params) ->
|
|
|
99 |
poolboy:transaction(PoolName, fun(Worker) ->
|
|
|
100 |
gen_server:call(Worker, {equery, Stmt, Params})
|
|
|
101 |
end).
|
|
|
102 |
```
|
|
|
103 |
|
|
|
104 |
### example_worker.erl
|
|
|
105 |
|
|
|
106 |
```erlang
|
|
|
107 |
-module(example_worker).
|
|
|
108 |
-behaviour(gen_server).
|
|
|
109 |
-behaviour(poolboy_worker).
|
|
|
110 |
|
|
|
111 |
-export([start_link/1]).
|
|
|
112 |
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
|
|
|
113 |
code_change/3]).
|
|
|
114 |
|
|
|
115 |
-record(state, {conn}).
|
|
|
116 |
|
|
|
117 |
start_link(Args) ->
|
|
|
118 |
gen_server:start_link(?MODULE, Args, []).
|
|
|
119 |
|
|
|
120 |
init(Args) ->
|
|
|
121 |
process_flag(trap_exit, true),
|
|
|
122 |
Hostname = proplists:get_value(hostname, Args),
|
|
|
123 |
Database = proplists:get_value(database, Args),
|
|
|
124 |
Username = proplists:get_value(username, Args),
|
|
|
125 |
Password = proplists:get_value(password, Args),
|
|
|
126 |
{ok, Conn} = epgsql:connect(Hostname, Username, Password, [
|
|
|
127 |
{database, Database}
|
|
|
128 |
]),
|
|
|
129 |
{ok, #state{conn=Conn}}.
|
|
|
130 |
|
|
|
131 |
handle_call({squery, Sql}, _From, #state{conn=Conn}=State) ->
|
|
|
132 |
{reply, epgsql:squery(Conn, Sql), State};
|
|
|
133 |
handle_call({equery, Stmt, Params}, _From, #state{conn=Conn}=State) ->
|
|
|
134 |
{reply, epgsql:equery(Conn, Stmt, Params), State};
|
|
|
135 |
handle_call(_Request, _From, State) ->
|
|
|
136 |
{reply, ok, State}.
|
|
|
137 |
|
|
|
138 |
handle_cast(_Msg, State) ->
|
|
|
139 |
{noreply, State}.
|
|
|
140 |
|
|
|
141 |
handle_info(_Info, State) ->
|
|
|
142 |
{noreply, State}.
|
|
|
143 |
|
|
|
144 |
terminate(_Reason, #state{conn=Conn}) ->
|
|
|
145 |
ok = epgsql:close(Conn),
|
|
|
146 |
ok.
|
|
|
147 |
|
|
|
148 |
code_change(_OldVsn, State, _Extra) ->
|
|
|
149 |
{ok, State}.
|
|
|
150 |
```
|
|
|
151 |
|
|
|
152 |
## Options
|
|
|
153 |
|
|
|
154 |
- `name`: the pool name
|
|
|
155 |
- `worker_module`: the module that represents the workers
|
|
|
156 |
- `size`: maximum pool size
|
|
|
157 |
- `max_overflow`: maximum number of workers created if pool is empty
|
|
|
158 |
- `strategy`: `lifo` or `fifo`, determines whether checked in workers should be
|
|
|
159 |
placed first or last in the line of available workers. So, `lifo` operates like a traditional stack; `fifo` like a queue. Default is `lifo`.
|
|
|
160 |
|
|
|
161 |
## Authors
|
|
|
162 |
|
|
|
163 |
- Devin Torres (devinus) <devin@devintorres.com>
|
|
|
164 |
- Andrew Thompson (Vagabond) <andrew@hijacked.us>
|
|
|
165 |
- Kurt Williams (onkel-dirtus) <kurt.r.williams@gmail.com>
|
|
|
166 |
|
|
|
167 |
## License
|
|
|
168 |
|
|
|
169 |
Poolboy is available in the public domain (see `UNLICENSE`).
|
|
|
170 |
Poolboy is also optionally available under the ISC license (see `LICENSE`),
|
|
|
171 |
meant especially for jurisdictions that do not recognize public domain works.
|