6 |
7u83 |
1 |
-module(poolboy_tests).
|
|
|
2 |
|
|
|
3 |
-include_lib("eunit/include/eunit.hrl").
|
|
|
4 |
|
|
|
5 |
pool_test_() ->
|
|
|
6 |
{foreach,
|
|
|
7 |
fun() ->
|
|
|
8 |
error_logger:tty(false)
|
|
|
9 |
end,
|
|
|
10 |
fun(_) ->
|
|
|
11 |
case whereis(poolboy_test) of
|
|
|
12 |
undefined -> ok;
|
|
|
13 |
Pid -> pool_call(Pid, stop)
|
|
|
14 |
end,
|
|
|
15 |
error_logger:tty(true)
|
|
|
16 |
end,
|
|
|
17 |
[
|
|
|
18 |
{<<"Basic pool operations">>,
|
|
|
19 |
fun pool_startup/0
|
|
|
20 |
},
|
|
|
21 |
{<<"Pool overflow should work">>,
|
|
|
22 |
fun pool_overflow/0
|
|
|
23 |
},
|
|
|
24 |
{<<"Pool behaves when empty">>,
|
|
|
25 |
fun pool_empty/0
|
|
|
26 |
},
|
|
|
27 |
{<<"Pool behaves when empty and oveflow is disabled">>,
|
|
|
28 |
fun pool_empty_no_overflow/0
|
|
|
29 |
},
|
|
|
30 |
{<<"Pool behaves on worker death">>,
|
|
|
31 |
fun worker_death/0
|
|
|
32 |
},
|
|
|
33 |
{<<"Pool behaves when full and a worker dies">>,
|
|
|
34 |
fun worker_death_while_full/0
|
|
|
35 |
},
|
|
|
36 |
{<<"Pool behaves when full, a worker dies and overflow disabled">>,
|
|
|
37 |
fun worker_death_while_full_no_overflow/0
|
|
|
38 |
},
|
|
|
39 |
{<<"Non-blocking pool behaves when full and overflow disabled">>,
|
|
|
40 |
fun pool_full_nonblocking_no_overflow/0
|
|
|
41 |
},
|
|
|
42 |
{<<"Non-blocking pool behaves when full">>,
|
|
|
43 |
fun pool_full_nonblocking/0
|
|
|
44 |
},
|
|
|
45 |
{<<"Pool behaves on owner death">>,
|
|
|
46 |
fun owner_death/0
|
|
|
47 |
},
|
|
|
48 |
{<<"Worker checked-in after an exception in a transaction">>,
|
|
|
49 |
fun checkin_after_exception_in_transaction/0
|
|
|
50 |
},
|
|
|
51 |
{<<"Pool returns status">>,
|
|
|
52 |
fun pool_returns_status/0
|
|
|
53 |
},
|
|
|
54 |
{<<"Pool demonitors previously waiting processes">>,
|
|
|
55 |
fun demonitors_previously_waiting_processes/0
|
|
|
56 |
},
|
|
|
57 |
{<<"Pool demonitors when a checkout is cancelled">>,
|
|
|
58 |
fun demonitors_when_checkout_cancelled/0
|
|
|
59 |
},
|
|
|
60 |
{<<"Check that LIFO is the default strategy">>,
|
|
|
61 |
fun default_strategy_lifo/0
|
|
|
62 |
},
|
|
|
63 |
{<<"Check LIFO strategy">>,
|
|
|
64 |
fun lifo_strategy/0
|
|
|
65 |
},
|
|
|
66 |
{<<"Check FIFO strategy">>,
|
|
|
67 |
fun fifo_strategy/0
|
|
|
68 |
},
|
|
|
69 |
{<<"Pool reuses waiting monitor when a worker exits">>,
|
|
|
70 |
fun reuses_waiting_monitor_on_worker_exit/0
|
|
|
71 |
},
|
|
|
72 |
{<<"Recover from timeout without exit handling">>,
|
|
|
73 |
fun transaction_timeout_without_exit/0},
|
|
|
74 |
{<<"Recover from transaction timeout">>,
|
|
|
75 |
fun transaction_timeout/0}
|
|
|
76 |
]
|
|
|
77 |
}.
|
|
|
78 |
|
|
|
79 |
%% Tell a worker to exit and await its impending doom.
|
|
|
80 |
kill_worker(Pid) ->
|
|
|
81 |
erlang:monitor(process, Pid),
|
|
|
82 |
pool_call(Pid, die),
|
|
|
83 |
receive
|
|
|
84 |
{'DOWN', _, process, Pid, _} ->
|
|
|
85 |
ok
|
|
|
86 |
end.
|
|
|
87 |
|
|
|
88 |
checkin_worker(Pid, Worker) ->
|
|
|
89 |
%% There's no easy way to wait for a checkin to complete, because it's
|
|
|
90 |
%% async and the supervisor may kill the process if it was an overflow
|
|
|
91 |
%% worker. The only solution seems to be a nasty hardcoded sleep.
|
|
|
92 |
poolboy:checkin(Pid, Worker),
|
|
|
93 |
timer:sleep(500).
|
|
|
94 |
|
|
|
95 |
|
|
|
96 |
transaction_timeout_without_exit() ->
|
|
|
97 |
{ok, Pid} = new_pool(1, 0),
|
|
|
98 |
?assertEqual({ready,1,0,0}, pool_call(Pid, status)),
|
|
|
99 |
WorkerList = pool_call(Pid, get_all_workers),
|
|
|
100 |
?assertMatch([_], WorkerList),
|
|
|
101 |
spawn(poolboy, transaction, [Pid,
|
|
|
102 |
fun(Worker) ->
|
|
|
103 |
ok = pool_call(Worker, work)
|
|
|
104 |
end,
|
|
|
105 |
0]),
|
|
|
106 |
timer:sleep(100),
|
|
|
107 |
?assertEqual(WorkerList, pool_call(Pid, get_all_workers)),
|
|
|
108 |
?assertEqual({ready,1,0,0}, pool_call(Pid, status)).
|
|
|
109 |
|
|
|
110 |
|
|
|
111 |
transaction_timeout() ->
|
|
|
112 |
{ok, Pid} = new_pool(1, 0),
|
|
|
113 |
?assertEqual({ready,1,0,0}, pool_call(Pid, status)),
|
|
|
114 |
WorkerList = pool_call(Pid, get_all_workers),
|
|
|
115 |
?assertMatch([_], WorkerList),
|
|
|
116 |
?assertExit(
|
|
|
117 |
{timeout, _},
|
|
|
118 |
poolboy:transaction(Pid,
|
|
|
119 |
fun(Worker) ->
|
|
|
120 |
ok = pool_call(Worker, work)
|
|
|
121 |
end,
|
|
|
122 |
0)),
|
|
|
123 |
?assertEqual(WorkerList, pool_call(Pid, get_all_workers)),
|
|
|
124 |
?assertEqual({ready,1,0,0}, pool_call(Pid, status)).
|
|
|
125 |
|
|
|
126 |
|
|
|
127 |
pool_startup() ->
|
|
|
128 |
%% Check basic pool operation.
|
|
|
129 |
{ok, Pid} = new_pool(10, 5),
|
|
|
130 |
?assertEqual(10, length(pool_call(Pid, get_avail_workers))),
|
|
|
131 |
poolboy:checkout(Pid),
|
|
|
132 |
?assertEqual(9, length(pool_call(Pid, get_avail_workers))),
|
|
|
133 |
Worker = poolboy:checkout(Pid),
|
|
|
134 |
?assertEqual(8, length(pool_call(Pid, get_avail_workers))),
|
|
|
135 |
checkin_worker(Pid, Worker),
|
|
|
136 |
?assertEqual(9, length(pool_call(Pid, get_avail_workers))),
|
|
|
137 |
?assertEqual(1, length(pool_call(Pid, get_all_monitors))),
|
|
|
138 |
ok = pool_call(Pid, stop).
|
|
|
139 |
|
|
|
140 |
pool_overflow() ->
|
|
|
141 |
%% Check that the pool overflows properly.
|
|
|
142 |
{ok, Pid} = new_pool(5, 5),
|
|
|
143 |
Workers = [poolboy:checkout(Pid) || _ <- lists:seq(0, 6)],
|
|
|
144 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
145 |
?assertEqual(7, length(pool_call(Pid, get_all_workers))),
|
|
|
146 |
[A, B, C, D, E, F, G] = Workers,
|
|
|
147 |
checkin_worker(Pid, A),
|
|
|
148 |
checkin_worker(Pid, B),
|
|
|
149 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
150 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
151 |
checkin_worker(Pid, C),
|
|
|
152 |
checkin_worker(Pid, D),
|
|
|
153 |
?assertEqual(2, length(pool_call(Pid, get_avail_workers))),
|
|
|
154 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
155 |
checkin_worker(Pid, E),
|
|
|
156 |
checkin_worker(Pid, F),
|
|
|
157 |
?assertEqual(4, length(pool_call(Pid, get_avail_workers))),
|
|
|
158 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
159 |
checkin_worker(Pid, G),
|
|
|
160 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
161 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
162 |
?assertEqual(0, length(pool_call(Pid, get_all_monitors))),
|
|
|
163 |
ok = pool_call(Pid, stop).
|
|
|
164 |
|
|
|
165 |
pool_empty() ->
|
|
|
166 |
%% Checks that the the pool handles the empty condition correctly when
|
|
|
167 |
%% overflow is enabled.
|
|
|
168 |
{ok, Pid} = new_pool(5, 2),
|
|
|
169 |
Workers = [poolboy:checkout(Pid) || _ <- lists:seq(0, 6)],
|
|
|
170 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
171 |
?assertEqual(7, length(pool_call(Pid, get_all_workers))),
|
|
|
172 |
[A, B, C, D, E, F, G] = Workers,
|
|
|
173 |
Self = self(),
|
|
|
174 |
spawn(fun() ->
|
|
|
175 |
Worker = poolboy:checkout(Pid),
|
|
|
176 |
Self ! got_worker,
|
|
|
177 |
checkin_worker(Pid, Worker)
|
|
|
178 |
end),
|
|
|
179 |
|
|
|
180 |
%% Spawned process should block waiting for worker to be available.
|
|
|
181 |
receive
|
|
|
182 |
got_worker -> ?assert(false)
|
|
|
183 |
after
|
|
|
184 |
500 -> ?assert(true)
|
|
|
185 |
end,
|
|
|
186 |
checkin_worker(Pid, A),
|
|
|
187 |
checkin_worker(Pid, B),
|
|
|
188 |
|
|
|
189 |
%% Spawned process should have been able to obtain a worker.
|
|
|
190 |
receive
|
|
|
191 |
got_worker -> ?assert(true)
|
|
|
192 |
after
|
|
|
193 |
500 -> ?assert(false)
|
|
|
194 |
end,
|
|
|
195 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
196 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
197 |
checkin_worker(Pid, C),
|
|
|
198 |
checkin_worker(Pid, D),
|
|
|
199 |
?assertEqual(2, length(pool_call(Pid, get_avail_workers))),
|
|
|
200 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
201 |
checkin_worker(Pid, E),
|
|
|
202 |
checkin_worker(Pid, F),
|
|
|
203 |
?assertEqual(4, length(pool_call(Pid, get_avail_workers))),
|
|
|
204 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
205 |
checkin_worker(Pid, G),
|
|
|
206 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
207 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
208 |
?assertEqual(0, length(pool_call(Pid, get_all_monitors))),
|
|
|
209 |
ok = pool_call(Pid, stop).
|
|
|
210 |
|
|
|
211 |
pool_empty_no_overflow() ->
|
|
|
212 |
%% Checks the pool handles the empty condition properly when overflow is
|
|
|
213 |
%% disabled.
|
|
|
214 |
{ok, Pid} = new_pool(5, 0),
|
|
|
215 |
Workers = [poolboy:checkout(Pid) || _ <- lists:seq(0, 4)],
|
|
|
216 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
217 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
218 |
[A, B, C, D, E] = Workers,
|
|
|
219 |
Self = self(),
|
|
|
220 |
spawn(fun() ->
|
|
|
221 |
Worker = poolboy:checkout(Pid),
|
|
|
222 |
Self ! got_worker,
|
|
|
223 |
checkin_worker(Pid, Worker)
|
|
|
224 |
end),
|
|
|
225 |
|
|
|
226 |
%% Spawned process should block waiting for worker to be available.
|
|
|
227 |
receive
|
|
|
228 |
got_worker -> ?assert(false)
|
|
|
229 |
after
|
|
|
230 |
500 -> ?assert(true)
|
|
|
231 |
end,
|
|
|
232 |
checkin_worker(Pid, A),
|
|
|
233 |
checkin_worker(Pid, B),
|
|
|
234 |
|
|
|
235 |
%% Spawned process should have been able to obtain a worker.
|
|
|
236 |
receive
|
|
|
237 |
got_worker -> ?assert(true)
|
|
|
238 |
after
|
|
|
239 |
500 -> ?assert(false)
|
|
|
240 |
end,
|
|
|
241 |
?assertEqual(2, length(pool_call(Pid, get_avail_workers))),
|
|
|
242 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
243 |
checkin_worker(Pid, C),
|
|
|
244 |
checkin_worker(Pid, D),
|
|
|
245 |
?assertEqual(4, length(pool_call(Pid, get_avail_workers))),
|
|
|
246 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
247 |
checkin_worker(Pid, E),
|
|
|
248 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
249 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
250 |
?assertEqual(0, length(pool_call(Pid, get_all_monitors))),
|
|
|
251 |
ok = pool_call(Pid, stop).
|
|
|
252 |
|
|
|
253 |
worker_death() ->
|
|
|
254 |
%% Check that dead workers are only restarted when the pool is not full
|
|
|
255 |
%% and the overflow count is 0. Meaning, don't restart overflow workers.
|
|
|
256 |
{ok, Pid} = new_pool(5, 2),
|
|
|
257 |
Worker = poolboy:checkout(Pid),
|
|
|
258 |
kill_worker(Worker),
|
|
|
259 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
260 |
[A, B, C|_Workers] = [poolboy:checkout(Pid) || _ <- lists:seq(0, 6)],
|
|
|
261 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
262 |
?assertEqual(7, length(pool_call(Pid, get_all_workers))),
|
|
|
263 |
kill_worker(A),
|
|
|
264 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
265 |
?assertEqual(6, length(pool_call(Pid, get_all_workers))),
|
|
|
266 |
kill_worker(B),
|
|
|
267 |
kill_worker(C),
|
|
|
268 |
?assertEqual(1, length(pool_call(Pid, get_avail_workers))),
|
|
|
269 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
270 |
?assertEqual(4, length(pool_call(Pid, get_all_monitors))),
|
|
|
271 |
ok = pool_call(Pid, stop).
|
|
|
272 |
|
|
|
273 |
worker_death_while_full() ->
|
|
|
274 |
%% Check that if a worker dies while the pool is full and there is a
|
|
|
275 |
%% queued checkout, a new worker is started and the checkout serviced.
|
|
|
276 |
%% If there are no queued checkouts, a new worker is not started.
|
|
|
277 |
{ok, Pid} = new_pool(5, 2),
|
|
|
278 |
Worker = poolboy:checkout(Pid),
|
|
|
279 |
kill_worker(Worker),
|
|
|
280 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
281 |
[A, B|_Workers] = [poolboy:checkout(Pid) || _ <- lists:seq(0, 6)],
|
|
|
282 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
283 |
?assertEqual(7, length(pool_call(Pid, get_all_workers))),
|
|
|
284 |
Self = self(),
|
|
|
285 |
spawn(fun() ->
|
|
|
286 |
poolboy:checkout(Pid),
|
|
|
287 |
Self ! got_worker,
|
|
|
288 |
%% XXX: Don't release the worker. We want to also test what happens
|
|
|
289 |
%% when the worker pool is full and a worker dies with no queued
|
|
|
290 |
%% checkouts.
|
|
|
291 |
timer:sleep(5000)
|
|
|
292 |
end),
|
|
|
293 |
|
|
|
294 |
%% Spawned process should block waiting for worker to be available.
|
|
|
295 |
receive
|
|
|
296 |
got_worker -> ?assert(false)
|
|
|
297 |
after
|
|
|
298 |
500 -> ?assert(true)
|
|
|
299 |
end,
|
|
|
300 |
kill_worker(A),
|
|
|
301 |
|
|
|
302 |
%% Spawned process should have been able to obtain a worker.
|
|
|
303 |
receive
|
|
|
304 |
got_worker -> ?assert(true)
|
|
|
305 |
after
|
|
|
306 |
1000 -> ?assert(false)
|
|
|
307 |
end,
|
|
|
308 |
kill_worker(B),
|
|
|
309 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
310 |
?assertEqual(6, length(pool_call(Pid, get_all_workers))),
|
|
|
311 |
?assertEqual(6, length(pool_call(Pid, get_all_monitors))),
|
|
|
312 |
ok = pool_call(Pid, stop).
|
|
|
313 |
|
|
|
314 |
worker_death_while_full_no_overflow() ->
|
|
|
315 |
%% Check that if a worker dies while the pool is full and there's no
|
|
|
316 |
%% overflow, a new worker is started unconditionally and any queued
|
|
|
317 |
%% checkouts are serviced.
|
|
|
318 |
{ok, Pid} = new_pool(5, 0),
|
|
|
319 |
Worker = poolboy:checkout(Pid),
|
|
|
320 |
kill_worker(Worker),
|
|
|
321 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
322 |
[A, B, C|_Workers] = [poolboy:checkout(Pid) || _ <- lists:seq(0, 4)],
|
|
|
323 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
324 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
325 |
Self = self(),
|
|
|
326 |
spawn(fun() ->
|
|
|
327 |
poolboy:checkout(Pid),
|
|
|
328 |
Self ! got_worker,
|
|
|
329 |
%% XXX: Do not release, need to also test when worker dies and no
|
|
|
330 |
%% checkouts queued.
|
|
|
331 |
timer:sleep(5000)
|
|
|
332 |
end),
|
|
|
333 |
|
|
|
334 |
%% Spawned process should block waiting for worker to be available.
|
|
|
335 |
receive
|
|
|
336 |
got_worker -> ?assert(false)
|
|
|
337 |
after
|
|
|
338 |
500 -> ?assert(true)
|
|
|
339 |
end,
|
|
|
340 |
kill_worker(A),
|
|
|
341 |
|
|
|
342 |
%% Spawned process should have been able to obtain a worker.
|
|
|
343 |
receive
|
|
|
344 |
got_worker -> ?assert(true)
|
|
|
345 |
after
|
|
|
346 |
1000 -> ?assert(false)
|
|
|
347 |
end,
|
|
|
348 |
kill_worker(B),
|
|
|
349 |
?assertEqual(1, length(pool_call(Pid, get_avail_workers))),
|
|
|
350 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
351 |
kill_worker(C),
|
|
|
352 |
?assertEqual(2, length(pool_call(Pid, get_avail_workers))),
|
|
|
353 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
354 |
?assertEqual(3, length(pool_call(Pid, get_all_monitors))),
|
|
|
355 |
ok = pool_call(Pid, stop).
|
|
|
356 |
|
|
|
357 |
pool_full_nonblocking_no_overflow() ->
|
|
|
358 |
%% Check that when the pool is full, checkouts return 'full' when the
|
|
|
359 |
%% option to use non-blocking checkouts is used.
|
|
|
360 |
{ok, Pid} = new_pool(5, 0),
|
|
|
361 |
Workers = [poolboy:checkout(Pid) || _ <- lists:seq(0, 4)],
|
|
|
362 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
363 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
364 |
?assertEqual(full, poolboy:checkout(Pid, false)),
|
|
|
365 |
?assertEqual(full, poolboy:checkout(Pid, false)),
|
|
|
366 |
A = hd(Workers),
|
|
|
367 |
checkin_worker(Pid, A),
|
|
|
368 |
?assertEqual(A, poolboy:checkout(Pid)),
|
|
|
369 |
?assertEqual(5, length(pool_call(Pid, get_all_monitors))),
|
|
|
370 |
ok = pool_call(Pid, stop).
|
|
|
371 |
|
|
|
372 |
pool_full_nonblocking() ->
|
|
|
373 |
%% Check that when the pool is full, checkouts return 'full' when the
|
|
|
374 |
%% option to use non-blocking checkouts is used.
|
|
|
375 |
{ok, Pid} = new_pool(5, 5),
|
|
|
376 |
Workers = [poolboy:checkout(Pid) || _ <- lists:seq(0, 9)],
|
|
|
377 |
?assertEqual(0, length(pool_call(Pid, get_avail_workers))),
|
|
|
378 |
?assertEqual(10, length(pool_call(Pid, get_all_workers))),
|
|
|
379 |
?assertEqual(full, poolboy:checkout(Pid, false)),
|
|
|
380 |
A = hd(Workers),
|
|
|
381 |
checkin_worker(Pid, A),
|
|
|
382 |
NewWorker = poolboy:checkout(Pid, false),
|
|
|
383 |
?assertEqual(false, is_process_alive(A)), %% Overflow workers get shutdown
|
|
|
384 |
?assert(is_pid(NewWorker)),
|
|
|
385 |
?assertEqual(full, poolboy:checkout(Pid, false)),
|
|
|
386 |
?assertEqual(10, length(pool_call(Pid, get_all_monitors))),
|
|
|
387 |
ok = pool_call(Pid, stop).
|
|
|
388 |
|
|
|
389 |
owner_death() ->
|
|
|
390 |
%% Check that a dead owner (a process that dies with a worker checked out)
|
|
|
391 |
%% causes the pool to dismiss the worker and prune the state space.
|
|
|
392 |
{ok, Pid} = new_pool(5, 5),
|
|
|
393 |
spawn(fun() ->
|
|
|
394 |
poolboy:checkout(Pid),
|
|
|
395 |
receive after 500 -> exit(normal) end
|
|
|
396 |
end),
|
|
|
397 |
timer:sleep(1000),
|
|
|
398 |
?assertEqual(5, length(pool_call(Pid, get_avail_workers))),
|
|
|
399 |
?assertEqual(5, length(pool_call(Pid, get_all_workers))),
|
|
|
400 |
?assertEqual(0, length(pool_call(Pid, get_all_monitors))),
|
|
|
401 |
ok = pool_call(Pid, stop).
|
|
|
402 |
|
|
|
403 |
checkin_after_exception_in_transaction() ->
|
|
|
404 |
{ok, Pool} = new_pool(2, 0),
|
|
|
405 |
?assertEqual(2, length(pool_call(Pool, get_avail_workers))),
|
|
|
406 |
Tx = fun(Worker) ->
|
|
|
407 |
?assert(is_pid(Worker)),
|
|
|
408 |
?assertEqual(1, length(pool_call(Pool, get_avail_workers))),
|
|
|
409 |
throw(it_on_the_ground),
|
|
|
410 |
?assert(false)
|
|
|
411 |
end,
|
|
|
412 |
try
|
|
|
413 |
poolboy:transaction(Pool, Tx)
|
|
|
414 |
catch
|
|
|
415 |
throw:it_on_the_ground -> ok
|
|
|
416 |
end,
|
|
|
417 |
?assertEqual(2, length(pool_call(Pool, get_avail_workers))),
|
|
|
418 |
ok = pool_call(Pool, stop).
|
|
|
419 |
|
|
|
420 |
pool_returns_status() ->
|
|
|
421 |
{ok, Pool} = new_pool(2, 0),
|
|
|
422 |
?assertEqual({ready, 2, 0, 0}, poolboy:status(Pool)),
|
|
|
423 |
poolboy:checkout(Pool),
|
|
|
424 |
?assertEqual({ready, 1, 0, 1}, poolboy:status(Pool)),
|
|
|
425 |
poolboy:checkout(Pool),
|
|
|
426 |
?assertEqual({full, 0, 0, 2}, poolboy:status(Pool)),
|
|
|
427 |
ok = pool_call(Pool, stop),
|
|
|
428 |
|
|
|
429 |
{ok, Pool2} = new_pool(1, 1),
|
|
|
430 |
?assertEqual({ready, 1, 0, 0}, poolboy:status(Pool2)),
|
|
|
431 |
poolboy:checkout(Pool2),
|
|
|
432 |
?assertEqual({overflow, 0, 0, 1}, poolboy:status(Pool2)),
|
|
|
433 |
poolboy:checkout(Pool2),
|
|
|
434 |
?assertEqual({full, 0, 1, 2}, poolboy:status(Pool2)),
|
|
|
435 |
ok = pool_call(Pool2, stop),
|
|
|
436 |
|
|
|
437 |
{ok, Pool3} = new_pool(0, 2),
|
|
|
438 |
?assertEqual({overflow, 0, 0, 0}, poolboy:status(Pool3)),
|
|
|
439 |
poolboy:checkout(Pool3),
|
|
|
440 |
?assertEqual({overflow, 0, 1, 1}, poolboy:status(Pool3)),
|
|
|
441 |
poolboy:checkout(Pool3),
|
|
|
442 |
?assertEqual({full, 0, 2, 2}, poolboy:status(Pool3)),
|
|
|
443 |
ok = pool_call(Pool3, stop),
|
|
|
444 |
|
|
|
445 |
{ok, Pool4} = new_pool(0, 0),
|
|
|
446 |
?assertEqual({full, 0, 0, 0}, poolboy:status(Pool4)),
|
|
|
447 |
ok = pool_call(Pool4, stop).
|
|
|
448 |
|
|
|
449 |
demonitors_previously_waiting_processes() ->
|
|
|
450 |
{ok, Pool} = new_pool(1,0),
|
|
|
451 |
Self = self(),
|
|
|
452 |
Pid = spawn(fun() ->
|
|
|
453 |
W = poolboy:checkout(Pool),
|
|
|
454 |
Self ! ok,
|
|
|
455 |
timer:sleep(500),
|
|
|
456 |
poolboy:checkin(Pool, W),
|
|
|
457 |
receive ok -> ok end
|
|
|
458 |
end),
|
|
|
459 |
receive ok -> ok end,
|
|
|
460 |
Worker = poolboy:checkout(Pool),
|
|
|
461 |
?assertEqual(1, length(get_monitors(Pool))),
|
|
|
462 |
poolboy:checkin(Pool, Worker),
|
|
|
463 |
timer:sleep(500),
|
|
|
464 |
?assertEqual(0, length(get_monitors(Pool))),
|
|
|
465 |
Pid ! ok,
|
|
|
466 |
ok = pool_call(Pool, stop).
|
|
|
467 |
|
|
|
468 |
demonitors_when_checkout_cancelled() ->
|
|
|
469 |
{ok, Pool} = new_pool(1,0),
|
|
|
470 |
Self = self(),
|
|
|
471 |
Pid = spawn(fun() ->
|
|
|
472 |
poolboy:checkout(Pool),
|
|
|
473 |
_ = (catch poolboy:checkout(Pool, true, 1000)),
|
|
|
474 |
Self ! ok,
|
|
|
475 |
receive ok -> ok end
|
|
|
476 |
end),
|
|
|
477 |
timer:sleep(500),
|
|
|
478 |
?assertEqual(2, length(get_monitors(Pool))),
|
|
|
479 |
receive ok -> ok end,
|
|
|
480 |
?assertEqual(1, length(get_monitors(Pool))),
|
|
|
481 |
Pid ! ok,
|
|
|
482 |
ok = pool_call(Pool, stop).
|
|
|
483 |
|
|
|
484 |
default_strategy_lifo() ->
|
|
|
485 |
%% Default strategy is LIFO
|
|
|
486 |
{ok, Pid} = new_pool(2, 0),
|
|
|
487 |
Worker1 = poolboy:checkout(Pid),
|
|
|
488 |
ok = poolboy:checkin(Pid, Worker1),
|
|
|
489 |
Worker1 = poolboy:checkout(Pid),
|
|
|
490 |
poolboy:stop(Pid).
|
|
|
491 |
|
|
|
492 |
lifo_strategy() ->
|
|
|
493 |
{ok, Pid} = new_pool(2, 0, lifo),
|
|
|
494 |
Worker1 = poolboy:checkout(Pid),
|
|
|
495 |
ok = poolboy:checkin(Pid, Worker1),
|
|
|
496 |
Worker1 = poolboy:checkout(Pid),
|
|
|
497 |
poolboy:stop(Pid).
|
|
|
498 |
|
|
|
499 |
fifo_strategy() ->
|
|
|
500 |
{ok, Pid} = new_pool(2, 0, fifo),
|
|
|
501 |
Worker1 = poolboy:checkout(Pid),
|
|
|
502 |
ok = poolboy:checkin(Pid, Worker1),
|
|
|
503 |
Worker2 = poolboy:checkout(Pid),
|
|
|
504 |
?assert(Worker1 =/= Worker2),
|
|
|
505 |
Worker1 = poolboy:checkout(Pid),
|
|
|
506 |
poolboy:stop(Pid).
|
|
|
507 |
|
|
|
508 |
reuses_waiting_monitor_on_worker_exit() ->
|
|
|
509 |
{ok, Pool} = new_pool(1,0),
|
|
|
510 |
|
|
|
511 |
Self = self(),
|
|
|
512 |
Pid = spawn(fun() ->
|
|
|
513 |
Worker = poolboy:checkout(Pool),
|
|
|
514 |
Self ! {worker, Worker},
|
|
|
515 |
poolboy:checkout(Pool),
|
|
|
516 |
receive ok -> ok end
|
|
|
517 |
end),
|
|
|
518 |
|
|
|
519 |
Worker = receive {worker, Worker1} -> Worker1 end,
|
|
|
520 |
Ref = monitor(process, Worker),
|
|
|
521 |
exit(Worker, kill),
|
|
|
522 |
receive
|
|
|
523 |
{'DOWN', Ref, _, _, _} ->
|
|
|
524 |
ok
|
|
|
525 |
end,
|
|
|
526 |
|
|
|
527 |
?assertEqual(1, length(get_monitors(Pool))),
|
|
|
528 |
|
|
|
529 |
Pid ! ok,
|
|
|
530 |
ok = pool_call(Pool, stop).
|
|
|
531 |
|
|
|
532 |
get_monitors(Pid) ->
|
|
|
533 |
%% Synchronise with the Pid to ensure it has handled all expected work.
|
|
|
534 |
_ = sys:get_status(Pid),
|
|
|
535 |
[{monitors, Monitors}] = erlang:process_info(Pid, [monitors]),
|
|
|
536 |
Monitors.
|
|
|
537 |
|
|
|
538 |
new_pool(Size, MaxOverflow) ->
|
|
|
539 |
poolboy:start_link([{name, {local, poolboy_test}},
|
|
|
540 |
{worker_module, poolboy_test_worker},
|
|
|
541 |
{size, Size}, {max_overflow, MaxOverflow}]).
|
|
|
542 |
|
|
|
543 |
new_pool(Size, MaxOverflow, Strategy) ->
|
|
|
544 |
poolboy:start_link([{name, {local, poolboy_test}},
|
|
|
545 |
{worker_module, poolboy_test_worker},
|
|
|
546 |
{size, Size}, {max_overflow, MaxOverflow},
|
|
|
547 |
{strategy, Strategy}]).
|
|
|
548 |
|
|
|
549 |
pool_call(ServerRef, Request) ->
|
|
|
550 |
gen_server:call(ServerRef, Request).
|