pooler_tests.erl 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303
  1. -module(pooler_tests).
  2. -include_lib("eunit/include/eunit.hrl").
  3. -include("../src/pooler.hrl").
  4. -compile([export_all]).
  5. % The `user' processes represent users of the pooler library. A user
  6. % process will take a pid, report details on the pid it has, release
  7. % and take a new pid, stop cleanly, and crash.
  8. start_user() ->
  9. spawn(fun() -> user_loop(start) end).
  10. user_id(Pid) ->
  11. Pid ! {get_tc_id, self()},
  12. receive
  13. {Type, Id} ->
  14. {Type, Id}
  15. end.
  16. user_new_tc(Pid) ->
  17. Pid ! new_tc.
  18. user_stop(Pid) ->
  19. Pid ! stop.
  20. user_crash(Pid) ->
  21. Pid ! crash.
  22. user_loop(Atom) when Atom =:= error_no_members orelse Atom =:= start ->
  23. user_loop(pooler:take_member(test_pool_1));
  24. user_loop(MyTC) ->
  25. receive
  26. {get_tc_id, From} ->
  27. From ! pooled_gs:get_id(MyTC),
  28. user_loop(MyTC);
  29. {ping_tc, From} ->
  30. From ! pooled_gs:ping(MyTC),
  31. user_loop(MyTC);
  32. {ping_count, From} ->
  33. From ! pooled_gs:ping_count(MyTC),
  34. user_loop(MyTC);
  35. new_tc ->
  36. pooler:return_member(test_pool_1, MyTC, ok),
  37. MyNewTC = pooler:take_member(test_pool_1),
  38. user_loop(MyNewTC);
  39. stop ->
  40. pooler:return_member(test_pool_1, MyTC, ok),
  41. stopped;
  42. crash ->
  43. erlang:error({user_loop, kaboom})
  44. end.
  45. % The `tc' processes represent the pids tracked by pooler for testing.
  46. % They have a type and an ID and can report their type and ID and
  47. % stop.
  48. tc_loop({Type, Id}) ->
  49. receive
  50. {get_id, From} ->
  51. From ! {ok, Type, Id},
  52. tc_loop({Type, Id});
  53. stop -> stopped;
  54. crash ->
  55. erlang:error({tc_loop, kaboom})
  56. end.
  57. get_tc_id(Pid) ->
  58. Pid ! {get_id, self()},
  59. receive
  60. {ok, Type, Id} ->
  61. {Type, Id}
  62. after 200 ->
  63. timeout
  64. end.
  65. stop_tc(Pid) ->
  66. Pid ! stop.
  67. tc_starter(Type) ->
  68. Ref = make_ref(),
  69. spawn_link(fun() -> tc_loop({Type, Ref}) end).
  70. assert_tc_valid(Pid) ->
  71. ?assertMatch({_Type, _Ref}, get_tc_id(Pid)),
  72. ok.
  73. % tc_sanity_test() ->
  74. % Pid1 = tc_starter("1"),
  75. % {"1", Id1} = get_tc_id(Pid1),
  76. % Pid2 = tc_starter("1"),
  77. % {"1", Id2} = get_tc_id(Pid2),
  78. % ?assertNot(Id1 == Id2),
  79. % stop_tc(Pid1),
  80. % stop_tc(Pid2).
  81. % user_sanity_test() ->
  82. % Pid1 = tc_starter("1"),
  83. % User = spawn(fun() -> user_loop(Pid1) end),
  84. % ?assertMatch({"1", _Ref}, user_id(User)),
  85. % user_crash(User),
  86. % stop_tc(Pid1).
  87. pooler_basics_via_config_test_() ->
  88. {setup,
  89. fun() ->
  90. application:set_env(pooler, metrics_module, fake_metrics),
  91. fake_metrics:start_link()
  92. end,
  93. fun(_X) ->
  94. fake_metrics:stop()
  95. end,
  96. {foreach,
  97. % setup
  98. fun() ->
  99. Pools = [[{name, test_pool_1},
  100. {max_count, 3},
  101. {init_count, 2},
  102. {cull_interval, {0, min}},
  103. {start_mfa,
  104. {pooled_gs, start_link, [{"type-0"}]}}]],
  105. application:set_env(pooler, pools, Pools),
  106. error_logger:delete_report_handler(error_logger_tty_h),
  107. application:start(pooler)
  108. end,
  109. fun(_X) ->
  110. application:stop(pooler)
  111. end,
  112. basic_tests()}}.
  113. pooler_basics_dynamic_test_() ->
  114. {setup,
  115. fun() ->
  116. application:set_env(pooler, metrics_module, fake_metrics),
  117. fake_metrics:start_link()
  118. end,
  119. fun(_X) ->
  120. fake_metrics:stop()
  121. end,
  122. {foreach,
  123. % setup
  124. fun() ->
  125. Pool = [{name, test_pool_1},
  126. {max_count, 3},
  127. {init_count, 2},
  128. {start_mfa,
  129. {pooled_gs, start_link, [{"type-0"}]}}],
  130. application:unset_env(pooler, pools),
  131. error_logger:delete_report_handler(error_logger_tty_h),
  132. application:start(pooler),
  133. pooler:new_pool(Pool)
  134. end,
  135. fun(_X) ->
  136. application:stop(pooler)
  137. end,
  138. basic_tests()}}.
  139. pooler_basics_integration_to_other_supervisor_test_() ->
  140. {setup,
  141. fun() ->
  142. application:set_env(pooler, metrics_module, fake_metrics),
  143. fake_metrics:start_link()
  144. end,
  145. fun(_X) ->
  146. fake_metrics:stop()
  147. end,
  148. {foreach,
  149. % setup
  150. fun() ->
  151. Pool = [{name, test_pool_1},
  152. {max_count, 3},
  153. {init_count, 2},
  154. {start_mfa,
  155. {pooled_gs, start_link, [{"type-0"}]}}],
  156. application:unset_env(pooler, pools),
  157. error_logger:delete_report_handler(error_logger_tty_h),
  158. application:start(pooler),
  159. supervisor:start_link(fake_external_supervisor, Pool)
  160. end,
  161. fun({ok, SupPid}) ->
  162. exit(SupPid, normal),
  163. application:stop(pooler)
  164. end,
  165. basic_tests()}}.
  166. basic_tests() ->
  167. [
  168. {"there are init_count members at start",
  169. fun() ->
  170. Stats = [ P || {P, {_, free, _}} <- pooler:pool_stats(test_pool_1) ],
  171. ?assertEqual(2, length(Stats))
  172. end},
  173. {"take and return one",
  174. fun() ->
  175. P = pooler:take_member(test_pool_1),
  176. ?assertMatch({"type-0", _Id}, pooled_gs:get_id(P)),
  177. ok = pooler:return_member(test_pool_1, P, ok)
  178. end},
  179. {"take and return one, named pool",
  180. fun() ->
  181. P = pooler:take_member(test_pool_1),
  182. ?assertMatch({"type-0", _Id}, pooled_gs:get_id(P)),
  183. ok, pooler:return_member(test_pool_1, P)
  184. end},
  185. {"attempt to take form unknown pool",
  186. fun() ->
  187. %% since pools are now servers, an unknown pool will timeout
  188. ?assertExit({noproc, _}, pooler:take_member(bad_pool_name))
  189. end},
  190. {"members creation is triggered after pool exhaustion until max",
  191. fun() ->
  192. %% init count is 2
  193. Pids0 = [pooler:take_member(test_pool_1), pooler:take_member(test_pool_1)],
  194. %% since new member creation is async, can only assert
  195. %% that we will get a pid, but may not be first try.
  196. Pids = get_n_pids(1, Pids0),
  197. %% pool is at max now, requests should give error
  198. ?assertEqual(error_no_members, pooler:take_member(test_pool_1)),
  199. ?assertEqual(error_no_members, pooler:take_member(test_pool_1)),
  200. PRefs = [ R || {_T, R} <- [ pooled_gs:get_id(P) || P <- Pids ] ],
  201. % no duplicates
  202. ?assertEqual(length(PRefs), length(lists:usort(PRefs)))
  203. end
  204. },
  205. {"pids are reused most recent return first",
  206. fun() ->
  207. P1 = pooler:take_member(test_pool_1),
  208. P2 = pooler:take_member(test_pool_1),
  209. ?assertNot(P1 == P2),
  210. ok = pooler:return_member(test_pool_1, P1, ok),
  211. ok = pooler:return_member(test_pool_1, P2, ok),
  212. % pids are reused most recent first
  213. ?assertEqual(P2, pooler:take_member(test_pool_1)),
  214. ?assertEqual(P1, pooler:take_member(test_pool_1))
  215. end},
  216. {"if an in-use pid crashes it is replaced",
  217. fun() ->
  218. Pids0 = get_n_pids(3, []),
  219. Ids0 = [ pooled_gs:get_id(P) || P <- Pids0 ],
  220. % crash them all
  221. [ pooled_gs:crash(P) || P <- Pids0 ],
  222. Pids1 = get_n_pids(3, []),
  223. Ids1 = [ pooled_gs:get_id(P) || P <- Pids1 ],
  224. [ ?assertNot(lists:member(I, Ids0)) || I <- Ids1 ]
  225. end
  226. },
  227. {"if a free pid crashes it is replaced",
  228. fun() ->
  229. FreePids = [ P || {P, {_, free, _}} <- pooler:pool_stats(test_pool_1) ],
  230. [ exit(P, kill) || P <- FreePids ],
  231. Pids1 = get_n_pids(3, []),
  232. ?assertEqual(3, length(Pids1))
  233. end},
  234. {"if a pid is returned with bad status it is replaced",
  235. fun() ->
  236. Pids0 = get_n_pids(3, []),
  237. Ids0 = [ pooled_gs:get_id(P) || P <- Pids0 ],
  238. % return them all marking as bad
  239. [ pooler:return_member(test_pool_1, P, fail) || P <- Pids0 ],
  240. Pids1 = get_n_pids(3, []),
  241. Ids1 = [ pooled_gs:get_id(P) || P <- Pids1 ],
  242. [ ?assertNot(lists:member(I, Ids0)) || I <- Ids1 ]
  243. end
  244. },
  245. {"if a consumer crashes, pid is replaced",
  246. fun() ->
  247. Consumer = start_user(),
  248. StartId = user_id(Consumer),
  249. user_crash(Consumer),
  250. NewPid = hd(get_n_pids(1, [])),
  251. NewId = pooled_gs:get_id(NewPid),
  252. ?assertNot(NewId == StartId)
  253. end
  254. },
  255. {"it is ok to return an unknown pid",
  256. fun() ->
  257. Bogus1 = spawn(fun() -> ok end),
  258. Bogus2 = spawn(fun() -> ok end),
  259. ?assertEqual(ok, pooler:return_member(test_pool_1, Bogus1, ok)),
  260. ?assertEqual(ok, pooler:return_member(test_pool_1, Bogus2, fail))
  261. end
  262. },
  263. {"it is ok to return a pid more than once",
  264. fun() ->
  265. M = pooler:take_member(test_pool_1),
  266. [ pooler:return_member(test_pool_1, M)
  267. || _I <- lists:seq(1, 37) ],
  268. M1 = pooler:take_member(test_pool_1),
  269. M2 = pooler:take_member(test_pool_1),
  270. ?assert(M1 =/= M2),
  271. Pool1 = gen_server:call(test_pool_1, dump_pool),
  272. ?assertEqual(2, Pool1#pool.in_use_count),
  273. ?assertEqual(0, Pool1#pool.free_count),
  274. pooler:return_member(test_pool_1, M1),
  275. pooler:return_member(test_pool_1, M2),
  276. Pool2 = gen_server:call(test_pool_1, dump_pool),
  277. ?assertEqual(0, Pool2#pool.in_use_count),
  278. ?assertEqual(2, Pool2#pool.free_count),
  279. ok
  280. end},
  281. {"calling return_member on error_no_members is ignored",
  282. fun() ->
  283. ?assertEqual(ok, pooler:return_member(test_pool_1, error_no_members)),
  284. ?assertEqual(ok, pooler:return_member(test_pool_1, error_no_members, ok)),
  285. ?assertEqual(ok, pooler:return_member(test_pool_1, error_no_members, fail))
  286. end
  287. },
  288. {"dynamic pool creation",
  289. fun() ->
  290. PoolSpec = [{name, dyn_pool_1},
  291. {max_count, 3},
  292. {init_count, 2},
  293. {start_mfa,
  294. {pooled_gs, start_link, [{"dyn-0"}]}}],
  295. {ok, SupPid1} = pooler:new_pool(PoolSpec),
  296. ?assert(is_pid(SupPid1)),
  297. M = pooler:take_member(dyn_pool_1),
  298. ?assertMatch({"dyn-0", _Id}, pooled_gs:get_id(M)),
  299. ?assertEqual(ok, pooler:rm_pool(dyn_pool_1)),
  300. ?assertExit({noproc, _}, pooler:take_member(dyn_pool_1)),
  301. %% verify pool of same name can be created after removal
  302. {ok, SupPid2} = pooler:new_pool(PoolSpec),
  303. ?assert(is_pid(SupPid2)),
  304. %% remove non-existing pool
  305. ?assertEqual(ok, pooler:rm_pool(dyn_pool_X)),
  306. ?assertEqual(ok, pooler:rm_pool(dyn_pool_1))
  307. end},
  308. {"metrics have been called (no timeout/queue)",
  309. fun() ->
  310. %% exercise the API to ensure we have certain keys reported as metrics
  311. fake_metrics:reset_metrics(),
  312. Pids = [ pooler:take_member(test_pool_1) || _I <- lists:seq(1, 10) ],
  313. [ pooler:return_member(test_pool_1, P) || P <- Pids ],
  314. catch pooler:take_member(bad_pool_name),
  315. %% kill and unused member
  316. exit(hd(Pids), kill),
  317. %% kill a used member
  318. KillMe = pooler:take_member(test_pool_1),
  319. exit(KillMe, kill),
  320. %% FIXME: We need to wait for pooler to process the
  321. %% exit message. This is ugly, will fix later.
  322. timer:sleep(200), % :(
  323. ExpectKeys = lists:sort([<<"pooler.test_pool_1.error_no_members_count">>,
  324. <<"pooler.test_pool_1.events">>,
  325. <<"pooler.test_pool_1.free_count">>,
  326. <<"pooler.test_pool_1.in_use_count">>,
  327. <<"pooler.test_pool_1.killed_free_count">>,
  328. <<"pooler.test_pool_1.killed_in_use_count">>,
  329. <<"pooler.test_pool_1.take_rate">>]),
  330. Metrics = fake_metrics:get_metrics(),
  331. GotKeys = lists:usort([ Name || {Name, _, _} <- Metrics ]),
  332. ?assertEqual(ExpectKeys, GotKeys)
  333. end},
  334. {"metrics have been called (with timeout/queue)",
  335. fun() ->
  336. %% exercise the API to ensure we have certain keys reported as metrics
  337. fake_metrics:reset_metrics(),
  338. %% pass a non-zero timeout here to exercise queueing
  339. Pids = [ pooler:take_member(test_pool_1, 1) || _I <- lists:seq(1, 10) ],
  340. [ pooler:return_member(test_pool_1, P) || P <- Pids ],
  341. catch pooler:take_member(bad_pool_name),
  342. %% kill and unused member
  343. exit(hd(Pids), kill),
  344. %% kill a used member
  345. KillMe = pooler:take_member(test_pool_1),
  346. exit(KillMe, kill),
  347. %% FIXME: We need to wait for pooler to process the
  348. %% exit message. This is ugly, will fix later.
  349. timer:sleep(200), % :(
  350. ExpectKeys = lists:sort([<<"pooler.test_pool_1.error_no_members_count">>,
  351. <<"pooler.test_pool_1.events">>,
  352. <<"pooler.test_pool_1.free_count">>,
  353. <<"pooler.test_pool_1.in_use_count">>,
  354. <<"pooler.test_pool_1.killed_free_count">>,
  355. <<"pooler.test_pool_1.killed_in_use_count">>,
  356. <<"pooler.test_pool_1.take_rate">>,
  357. <<"pooler.test_pool_1.queue_count">>]),
  358. Metrics = fake_metrics:get_metrics(),
  359. GotKeys = lists:usort([ Name || {Name, _, _} <- Metrics ]),
  360. ?assertEqual(ExpectKeys, GotKeys)
  361. end},
  362. {"accept bad member is handled",
  363. fun() ->
  364. Bad = spawn(fun() -> ok end),
  365. FakeStarter = spawn(fun() -> starter end),
  366. ?assertEqual(ok, pooler:accept_member(test_pool_1, {FakeStarter, Bad}))
  367. end},
  368. {"utilization returns sane results",
  369. fun() ->
  370. #pool{max_count = MaxCount, queue_max = QueueMax} = Pool = sys:get_state(test_pool_1),
  371. ?assertEqual(MaxCount, ?gv(max_count, pooler:pool_utilization(test_pool_1))),
  372. ?assertEqual(0, ?gv(in_use_count, pooler:pool_utilization(test_pool_1))),
  373. ?assertEqual(2, ?gv(free_count, pooler:pool_utilization(test_pool_1))),
  374. ?assertEqual(0, ?gv(queued_count, pooler:pool_utilization(test_pool_1))),
  375. ?assertEqual(QueueMax, ?gv(queue_max, pooler:pool_utilization(test_pool_1)))
  376. end}
  377. ].
  378. pooler_groups_test_() ->
  379. {setup,
  380. fun() ->
  381. application:set_env(pooler, metrics_module, fake_metrics),
  382. fake_metrics:start_link()
  383. end,
  384. fun(_X) ->
  385. fake_metrics:stop()
  386. end,
  387. {foreach,
  388. % setup
  389. fun() ->
  390. Pools = [[{name, test_pool_1},
  391. {group, group_1},
  392. {max_count, 3},
  393. {init_count, 2},
  394. {start_mfa,
  395. {pooled_gs, start_link, [{"type-1-1"}]}}],
  396. [{name, test_pool_2},
  397. {group, group_1},
  398. {max_count, 3},
  399. {init_count, 2},
  400. {start_mfa,
  401. {pooled_gs, start_link, [{"type-1-2"}]}}],
  402. %% test_pool_3 not part of the group
  403. [{name, test_pool_3},
  404. {group, undefined},
  405. {max_count, 3},
  406. {init_count, 2},
  407. {start_mfa,
  408. {pooled_gs, start_link, [{"type-3"}]}}]
  409. ],
  410. application:set_env(pooler, pools, Pools),
  411. %% error_logger:delete_report_handler(error_logger_tty_h),
  412. pg2:start(),
  413. application:start(pooler)
  414. end,
  415. fun(_X) ->
  416. application:stop(pooler),
  417. application:stop(pg2)
  418. end,
  419. [
  420. {"take and return one group member (repeated)",
  421. fun() ->
  422. Types = [ begin
  423. Pid = pooler:take_group_member(group_1),
  424. {Type, _} = pooled_gs:get_id(Pid),
  425. ?assertMatch("type-1" ++ _, Type),
  426. ok = pooler:return_group_member(group_1, Pid, ok),
  427. Type
  428. end
  429. || _I <- lists:seq(1, 50) ],
  430. Type_1_1 = [ X || "type-1-1" = X <- Types ],
  431. Type_1_2 = [ X || "type-1-2" = X <- Types ],
  432. ?assert(length(Type_1_1) > 0),
  433. ?assert(length(Type_1_2) > 0)
  434. end},
  435. {"take member from unknown group",
  436. fun() ->
  437. ?assertEqual({error_no_group, not_a_group},
  438. pooler:take_group_member(not_a_group))
  439. end},
  440. {"return member to unknown group",
  441. fun() ->
  442. Pid = pooler:take_group_member(group_1),
  443. ?assertEqual(ok, pooler:return_group_member(no_such_group, Pid))
  444. end},
  445. {"return member to wrong group",
  446. fun() ->
  447. Pid = pooler:take_member(test_pool_3),
  448. ?assertEqual(ok, pooler:return_group_member(group_1, Pid))
  449. end},
  450. {"return member with something which is not a pid",
  451. fun() ->
  452. ?assertException(error, _, pooler:return_group_member(group_1, not_pid))
  453. end},
  454. {"take member from empty group",
  455. fun() ->
  456. %% artificially empty group member list
  457. [ pg2:leave(group_1, M) || M <- pg2:get_members(group_1) ],
  458. ?assertEqual(error_no_members, pooler:take_group_member(group_1))
  459. end},
  460. {"return member to group, implied ok",
  461. fun() ->
  462. Pid = pooler:take_group_member(group_1),
  463. ?assertEqual(ok, pooler:return_group_member(group_1, Pid))
  464. end},
  465. {"return error_no_member to group",
  466. fun() ->
  467. ?assertEqual(ok, pooler:return_group_member(group_1, error_no_members))
  468. end},
  469. {"exhaust pools in group",
  470. fun() ->
  471. Pids = get_n_pids_group(group_1, 6, []),
  472. %% they should all be pids
  473. [ begin
  474. {Type, _} = pooled_gs:get_id(P),
  475. ?assertMatch("type-1" ++ _, Type),
  476. ok
  477. end || P <- Pids ],
  478. %% further attempts should be error
  479. [error_no_members,
  480. error_no_members,
  481. error_no_members] = [ pooler:take_group_member(group_1)
  482. || _I <- lists:seq(1, 3) ]
  483. end},
  484. {"rm_group with nonexisting group",
  485. fun() ->
  486. ?assertEqual(ok, pooler:rm_group(i_dont_exist))
  487. end},
  488. {"rm_group with existing empty group",
  489. fun() ->
  490. ?assertEqual(ok, pooler:rm_pool(test_pool_1)),
  491. ?assertEqual(ok, pooler:rm_pool(test_pool_2)),
  492. ?assertEqual(error_no_members, pooler:take_group_member(group_1)),
  493. ?assertEqual(ok, pooler:rm_group(group_1)),
  494. ?assertExit({noproc, _}, pooler:take_member(test_pool_1)),
  495. ?assertExit({noproc, _}, pooler:take_member(test_pool_2)),
  496. ?assertEqual({error_no_group, group_1},
  497. pooler:take_group_member(group_1))
  498. end},
  499. {"rm_group with existing non-empty group",
  500. fun() ->
  501. %% Verify that group members exist
  502. MemberPid = pooler:take_group_member(group_1),
  503. ?assert(is_pid(MemberPid)),
  504. pooler:return_group_member(group_1, MemberPid),
  505. Pool1Pid = pooler:take_member(test_pool_1),
  506. ?assert(is_pid(Pool1Pid)),
  507. pooler:return_member(test_pool_1, Pool1Pid),
  508. Pool2Pid = pooler:take_member(test_pool_2),
  509. ?assert(is_pid(Pool2Pid)),
  510. pooler:return_member(test_pool_2, Pool2Pid),
  511. %% Delete and verify that group and pools are destroyed
  512. ?assertEqual(ok, pooler:rm_group(group_1)),
  513. ?assertExit({noproc, _}, pooler:take_member(test_pool_1)),
  514. ?assertExit({noproc, _}, pooler:take_member(test_pool_2)),
  515. ?assertEqual({error_no_group, group_1},
  516. pooler:take_group_member(group_1))
  517. end}
  518. ]}}.
  519. pooler_limit_failed_adds_test_() ->
  520. %% verify that pooler crashes completely if too many failures are
  521. %% encountered while trying to add pids.
  522. {setup,
  523. fun() ->
  524. Pools = [[{name, test_pool_1},
  525. {max_count, 10},
  526. {init_count, 10},
  527. {start_mfa,
  528. {pooled_gs, start_link, [crash]}}]],
  529. application:set_env(pooler, pools, Pools)
  530. end,
  531. fun(_) ->
  532. application:stop(pooler)
  533. end,
  534. fun() ->
  535. application:start(pooler),
  536. ?assertEqual(error_no_members, pooler:take_member(test_pool_1)),
  537. ?assertEqual(error_no_members, pooler:take_member(test_pool_1))
  538. end}.
  539. pooler_scheduled_cull_test_() ->
  540. {setup,
  541. fun() ->
  542. application:set_env(pooler, metrics_module, fake_metrics),
  543. fake_metrics:start_link(),
  544. Pools = [[{name, test_pool_1},
  545. {max_count, 10},
  546. {init_count, 2},
  547. {start_mfa, {pooled_gs, start_link, [{"type-0"}]}},
  548. {cull_interval, {200, ms}},
  549. {max_age, {0, min}}]],
  550. application:set_env(pooler, pools, Pools),
  551. %% error_logger:delete_report_handler(error_logger_tty_h),
  552. application:start(pooler)
  553. end,
  554. fun(_X) ->
  555. fake_metrics:stop(),
  556. application:stop(pooler)
  557. end,
  558. [
  559. {foreach,
  560. fun() ->
  561. Pids = get_n_pids(test_pool_1, 10, []),
  562. ?assertEqual(10, length(pooler:pool_stats(test_pool_1))),
  563. ?assertEqual(10, length(Pids)),
  564. Pids
  565. end,
  566. fun(Pids) ->
  567. [ pooler:return_member(test_pool_1, P) || P <- Pids ]
  568. end,
  569. [
  570. fun(Pids) ->
  571. {"excess members are culled run 1",
  572. fun() ->
  573. [ pooler:return_member(test_pool_1, P) || P <- Pids ],
  574. %% wait for longer than cull delay
  575. timer:sleep(250),
  576. ?assertEqual(2, length(pooler:pool_stats(test_pool_1))),
  577. ?assertEqual(2, ?gv(free_count,pooler:pool_utilization(test_pool_1)))
  578. end}
  579. end,
  580. fun(Pids) ->
  581. {"excess members are culled run 2",
  582. fun() ->
  583. [ pooler:return_member(test_pool_1, P) || P <- Pids ],
  584. %% wait for longer than cull delay
  585. timer:sleep(250),
  586. ?assertEqual(2, length(pooler:pool_stats(test_pool_1))),
  587. ?assertEqual(2, ?gv(free_count,pooler:pool_utilization(test_pool_1)))
  588. end}
  589. end,
  590. fun(Pids) -> in_use_members_not_culled(Pids, 1) end,
  591. fun(Pids) -> in_use_members_not_culled(Pids, 2) end,
  592. fun(Pids) -> in_use_members_not_culled(Pids, 3) end,
  593. fun(Pids) -> in_use_members_not_culled(Pids, 4) end,
  594. fun(Pids) -> in_use_members_not_culled(Pids, 5) end,
  595. fun(Pids) -> in_use_members_not_culled(Pids, 6) end
  596. ]},
  597. {"no cull when init_count matches max_count",
  598. %% not sure how to verify this. But this test at least
  599. %% exercises the code path.
  600. fun() ->
  601. Config = [{name, test_static_pool_1},
  602. {max_count, 2},
  603. {init_count, 2},
  604. {start_mfa, {pooled_gs, start_link, [{"static-0"}]}},
  605. {cull_interval, {200, ms}}], % ignored
  606. pooler:new_pool(Config),
  607. P = pooler:take_member(test_static_pool_1),
  608. ?assertMatch({"static-0", _}, pooled_gs:get_id(P)),
  609. pooler:return_member(test_static_pool_1, P),
  610. ok
  611. end}
  612. ]}.
  613. in_use_members_not_culled(Pids, N) ->
  614. {"in-use members are not culled " ++ erlang:integer_to_list(N),
  615. fun() ->
  616. %% wait for longer than cull delay
  617. timer:sleep(250),
  618. PidCount = length(Pids),
  619. ?assertEqual(PidCount,
  620. length(pooler:pool_stats(test_pool_1))),
  621. ?assertEqual(0, ?gv(free_count,pooler:pool_utilization(test_pool_1))),
  622. ?assertEqual(PidCount, ?gv(in_use_count,pooler:pool_utilization(test_pool_1))),
  623. Returns = lists:sublist(Pids, N),
  624. [ pooler:return_member(test_pool_1, P)
  625. || P <- Returns ],
  626. timer:sleep(250),
  627. ?assertEqual(PidCount - N,
  628. length(pooler:pool_stats(test_pool_1)))
  629. end}.
  630. random_message_test_() ->
  631. {setup,
  632. fun() ->
  633. Pools = [[{name, test_pool_1},
  634. {max_count, 2},
  635. {init_count, 1},
  636. {start_mfa,
  637. {pooled_gs, start_link, [{"type-0"}]}}]],
  638. application:set_env(pooler, pools, Pools),
  639. error_logger:delete_report_handler(error_logger_tty_h),
  640. application:start(pooler),
  641. %% now send some bogus messages
  642. %% do the call in a throw-away process to avoid timeout error
  643. spawn(fun() -> catch gen_server:call(test_pool_1, {unexpected_garbage_msg, 5}) end),
  644. gen_server:cast(test_pool_1, {unexpected_garbage_msg, 6}),
  645. whereis(test_pool_1) ! {unexpected_garbage_msg, 7},
  646. ok
  647. end,
  648. fun(_) ->
  649. application:stop(pooler)
  650. end,
  651. [
  652. fun() ->
  653. Pid = spawn(fun() -> ok end),
  654. MonMsg = {'DOWN', erlang:make_ref(), process, Pid, because},
  655. test_pool_1 ! MonMsg
  656. end,
  657. fun() ->
  658. Pid = pooler:take_member(test_pool_1),
  659. {Type, _} = pooled_gs:get_id(Pid),
  660. ?assertEqual("type-0", Type)
  661. end,
  662. fun() ->
  663. RawPool = gen_server:call(test_pool_1, dump_pool),
  664. ?assertEqual(pool, element(1, RawPool))
  665. end
  666. ]}.
  667. pooler_integration_long_init_test_() ->
  668. {foreach,
  669. % setup
  670. fun() ->
  671. Pool = [{name, test_pool_1},
  672. {max_count, 10},
  673. {init_count, 0},
  674. {member_start_timeout, {10, ms}},
  675. {start_mfa,
  676. {pooled_gs, start_link, [{"type-0", fun() -> timer:sleep(15) end}]}}],
  677. application:set_env(pooler, pools, [Pool]),
  678. application:start(pooler)
  679. end,
  680. % cleanup
  681. fun(_) ->
  682. application:stop(pooler)
  683. end,
  684. %
  685. [
  686. fun(_) ->
  687. % Test what happens when pool members take too long to start.
  688. % The pooler_starter should kill off stale members, there by
  689. % reducing the number of children of the member_sup. This
  690. % activity occurs both during take member and accept member.
  691. % Accordingly, the count should go to zero once all starters
  692. % check in.
  693. fun() ->
  694. ?assertEqual(0, children_count(pooler_test_pool_1_member_sup)),
  695. [begin
  696. ?assertEqual(error_no_members, pooler:take_member(test_pool_1)),
  697. ?assertEqual(1, starting_members(test_pool_1))
  698. end
  699. || _ <- lists:seq(1,10)],
  700. ?assertEqual(10, children_count(pooler_test_pool_1_member_sup)),
  701. timer:sleep(150),
  702. ?assertEqual(0, children_count(pooler_test_pool_1_member_sup)),
  703. ?assertEqual(0, starting_members(test_pool_1))
  704. end
  705. end
  706. ]
  707. }.
  708. sleep_for_configured_timeout() ->
  709. SleepTime = case application:get_env(pooler, sleep_time) of
  710. {ok, Val} ->
  711. Val;
  712. _ ->
  713. 0
  714. end,
  715. timer:sleep(SleepTime).
  716. pooler_integration_queueing_test_() ->
  717. {foreach,
  718. % setup
  719. fun() ->
  720. Pool = [{name, test_pool_1},
  721. {max_count, 10},
  722. {queue_max, 10},
  723. {init_count, 0},
  724. {metrics, fake_metrics},
  725. {member_start_timeout, {5, sec}},
  726. {start_mfa,
  727. {pooled_gs, start_link, [
  728. {"type-0",
  729. fun pooler_tests:sleep_for_configured_timeout/0 }
  730. ]
  731. }
  732. }
  733. ],
  734. application:set_env(pooler, pools, [Pool]),
  735. fake_metrics:start_link(),
  736. application:start(pooler)
  737. end,
  738. % cleanup
  739. fun(_) ->
  740. fake_metrics:stop(),
  741. application:stop(pooler)
  742. end,
  743. [
  744. fun(_) ->
  745. fun() ->
  746. ?assertEqual(0, (dump_pool(test_pool_1))#pool.free_count),
  747. Val = pooler:take_member(test_pool_1, 10),
  748. ?assert(is_pid(Val)),
  749. pooler:return_member(test_pool_1, Val)
  750. end
  751. end,
  752. fun(_) ->
  753. fun() ->
  754. application:set_env(pooler, sleep_time, 1),
  755. ?assertEqual(0, (dump_pool(test_pool_1))#pool.free_count),
  756. Val = pooler:take_member(test_pool_1, 0),
  757. ?assertEqual(error_no_members, Val),
  758. ?assertEqual(0, ?gv(free_count,pooler:pool_utilization(test_pool_1))),
  759. timer:sleep(50),
  760. %Next request should be available
  761. Pid = pooler:take_member(test_pool_1, 0),
  762. ?assert(is_pid(Pid)),
  763. pooler:return_member(test_pool_1, Pid)
  764. end
  765. end,
  766. fun(_) ->
  767. fun() ->
  768. application:set_env(pooler, sleep_time, 10),
  769. ?assertEqual(0, (dump_pool(test_pool_1))#pool.free_count),
  770. [
  771. ?assertEqual(pooler:take_member(test_pool_1, 0), error_no_members) ||
  772. _ <- lists:seq(1, (dump_pool(test_pool_1))#pool.max_count)],
  773. timer:sleep(50),
  774. %Next request should be available
  775. Pid = pooler:take_member(test_pool_1, 0),
  776. ?assert(is_pid(Pid)),
  777. pooler:return_member(test_pool_1, Pid)
  778. end
  779. end,
  780. fun(_) ->
  781. fun() ->
  782. % fill to queue_max, next request should return immediately with no_members
  783. % Will return a if queue max is not enforced.
  784. application:set_env(pooler, sleep_time, 100),
  785. [ proc_lib:spawn(fun() ->
  786. Val = pooler:take_member(test_pool_1, 200),
  787. ?assert(is_pid(Val)),
  788. pooler:return_member(Val)
  789. end)
  790. || _ <- lists:seq(1, (dump_pool(test_pool_1))#pool.max_count)
  791. ],
  792. ?assertEqual(0, ?gv(free_count,pooler:pool_utilization(test_pool_1))),
  793. ?assert(?gv(queued_count,pooler:pool_utilization(test_pool_1)) >= 1),
  794. ?assertEqual(10, ?gv(queue_max,pooler:pool_utilization(test_pool_1))),
  795. timer:sleep(50),
  796. ?assertEqual(10, queue:len((dump_pool(test_pool_1))#pool.queued_requestors)),
  797. ?assertEqual(10, ?gv(queue_max,pooler:pool_utilization(test_pool_1))),
  798. ?assertEqual(pooler:take_member(test_pool_1, 500), error_no_members),
  799. ExpectKeys = lists:sort([<<"pooler.test_pool_1.error_no_members_count">>,
  800. <<"pooler.test_pool_1.events">>,
  801. <<"pooler.test_pool_1.take_rate">>,
  802. <<"pooler.test_pool_1.queue_count">>,
  803. <<"pooler.test_pool_1.queue_max_reached">>]),
  804. Metrics = fake_metrics:get_metrics(),
  805. GotKeys = lists:usort([ Name || {Name, _, _} <- Metrics ]),
  806. ?assertEqual(ExpectKeys, GotKeys),
  807. timer:sleep(100),
  808. Val = pooler:take_member(test_pool_1, 500),
  809. ?assert(is_pid(Val)),
  810. pooler:return_member(test_pool_1, Val)
  811. end
  812. end
  813. ]
  814. }.
  815. pooler_integration_queueing_return_member_test_() ->
  816. {foreach,
  817. % setup
  818. fun() ->
  819. Pool = [{name, test_pool_1},
  820. {max_count, 10},
  821. {queue_max, 10},
  822. {init_count, 10},
  823. {metrics, fake_metrics},
  824. {member_start_timeout, {5, sec}},
  825. {start_mfa,
  826. {pooled_gs, start_link, [
  827. {"type-0",
  828. fun pooler_tests:sleep_for_configured_timeout/0 }
  829. ]
  830. }
  831. }
  832. ],
  833. application:set_env(pooler, pools, [Pool]),
  834. fake_metrics:start_link(),
  835. application:start(pooler)
  836. end,
  837. % cleanup
  838. fun(_) ->
  839. fake_metrics:stop(),
  840. application:stop(pooler)
  841. end,
  842. [
  843. fun(_) ->
  844. fun() ->
  845. application:set_env(pooler, sleep_time, 0),
  846. Pids = [ proc_lib:spawn_link(fun() ->
  847. Val = pooler:take_member(test_pool_1, 200),
  848. ?assert(is_pid(Val)),
  849. receive
  850. _ ->
  851. pooler:return_member(test_pool_1, Val)
  852. after
  853. 5000 ->
  854. pooler:return_member(test_pool_1, Val)
  855. end
  856. end)
  857. || _ <- lists:seq(1, (dump_pool(test_pool_1))#pool.max_count)
  858. ],
  859. timer:sleep(1),
  860. Parent = self(),
  861. proc_lib:spawn_link(fun() ->
  862. Val = pooler:take_member(test_pool_1, 200),
  863. Parent ! Val
  864. end),
  865. [Pid ! return || Pid <- Pids],
  866. receive
  867. Result ->
  868. ?assert(is_pid(Result)),
  869. pooler:return_member(test_pool_1, Result)
  870. end,
  871. ?assertEqual((dump_pool(test_pool_1))#pool.max_count, length((dump_pool(test_pool_1))#pool.free_pids)),
  872. ?assertEqual((dump_pool(test_pool_1))#pool.max_count, (dump_pool(test_pool_1))#pool.free_count)
  873. end
  874. end
  875. ]
  876. }.
  877. pooler_integration_test_() ->
  878. {foreach,
  879. % setup
  880. fun() ->
  881. Pools = [[{name, test_pool_1},
  882. {max_count, 10},
  883. {init_count, 10},
  884. {start_mfa,
  885. {pooled_gs, start_link, [{"type-0"}]}}]],
  886. application:set_env(pooler, pools, Pools),
  887. error_logger:delete_report_handler(error_logger_tty_h),
  888. application:start(pooler),
  889. Users = [ start_user() || _X <- lists:seq(1, 10) ],
  890. Users
  891. end,
  892. % cleanup
  893. fun(Users) ->
  894. [ user_stop(U) || U <- Users ],
  895. application:stop(pooler)
  896. end,
  897. %
  898. [
  899. fun(Users) ->
  900. fun() ->
  901. % each user has a different tc ID
  902. TcIds = lists:sort([ user_id(UPid) || UPid <- Users ]),
  903. ?assertEqual(lists:usort(TcIds), TcIds)
  904. end
  905. end
  906. ,
  907. fun(Users) ->
  908. fun() ->
  909. % users still unique after a renew cycle
  910. [ user_new_tc(UPid) || UPid <- Users ],
  911. TcIds = lists:sort([ user_id(UPid) || UPid <- Users ]),
  912. ?assertEqual(lists:usort(TcIds), TcIds)
  913. end
  914. end
  915. ,
  916. fun(Users) ->
  917. fun() ->
  918. % all users crash, pids are replaced
  919. TcIds1 = lists:sort([ user_id(UPid) || UPid <- Users ]),
  920. [ user_crash(UPid) || UPid <- Users ],
  921. Seq = lists:seq(1, 5),
  922. Users2 = [ start_user() || _X <- Seq ],
  923. TcIds2 = lists:sort([ user_id(UPid) || UPid <- Users2 ]),
  924. Both =
  925. sets:to_list(sets:intersection([sets:from_list(TcIds1),
  926. sets:from_list(TcIds2)])),
  927. ?assertEqual([], Both)
  928. end
  929. end
  930. ]
  931. }.
  932. pooler_auto_grow_disabled_by_default_test_() ->
  933. {setup,
  934. fun() ->
  935. application:set_env(pooler, metrics_module, fake_metrics),
  936. fake_metrics:start_link()
  937. end,
  938. fun(_X) ->
  939. fake_metrics:stop()
  940. end,
  941. {foreach,
  942. % setup
  943. fun() ->
  944. Pool = [{name, test_pool_1},
  945. {max_count, 5},
  946. {init_count, 2},
  947. {start_mfa,
  948. {pooled_gs, start_link, [{"type-0"}]}}],
  949. application:unset_env(pooler, pools),
  950. error_logger:delete_report_handler(error_logger_tty_h),
  951. application:start(pooler),
  952. pooler:new_pool(Pool)
  953. end,
  954. fun(_X) ->
  955. application:stop(pooler)
  956. end,
  957. [
  958. {"take one, and it should not auto-grow",
  959. fun() ->
  960. ?assertEqual(2, (dump_pool(test_pool_1))#pool.free_count),
  961. P = pooler:take_member(test_pool_1),
  962. ?assertMatch({"type-0", _Id}, pooled_gs:get_id(P)),
  963. timer:sleep(100),
  964. ?assertEqual(1, (dump_pool(test_pool_1))#pool.free_count),
  965. ok, pooler:return_member(test_pool_1, P)
  966. end}
  967. ]}}.
  968. pooler_auto_grow_enabled_test_() ->
  969. {setup,
  970. fun() ->
  971. application:set_env(pooler, metrics_module, fake_metrics),
  972. fake_metrics:start_link()
  973. end,
  974. fun(_X) ->
  975. fake_metrics:stop()
  976. end,
  977. {foreach,
  978. % setup
  979. fun() ->
  980. Pool = [{name, test_pool_1},
  981. {max_count, 5},
  982. {init_count, 2},
  983. {auto_grow_threshold, 1},
  984. {start_mfa,
  985. {pooled_gs, start_link, [{"type-0"}]}}],
  986. application:unset_env(pooler, pools),
  987. error_logger:delete_report_handler(error_logger_tty_h),
  988. application:start(pooler),
  989. pooler:new_pool(Pool)
  990. end,
  991. fun(_X) ->
  992. application:stop(pooler)
  993. end,
  994. [
  995. {"take one, and it should grow by 2",
  996. fun() ->
  997. ?assertEqual(2, (dump_pool(test_pool_1))#pool.free_count),
  998. P = pooler:take_member(test_pool_1),
  999. ?assertMatch({"type-0", _Id}, pooled_gs:get_id(P)),
  1000. timer:sleep(100),
  1001. ?assertEqual(3, (dump_pool(test_pool_1))#pool.free_count),
  1002. ok, pooler:return_member(test_pool_1, P)
  1003. end}
  1004. ]}}.
  1005. pooler_custom_stop_mfa_test_() ->
  1006. {foreach,
  1007. fun() ->
  1008. Pool = [{name, test_pool_1},
  1009. {max_count, 3},
  1010. {init_count, 2},
  1011. {cull_interval, {200, ms}},
  1012. {max_age, {0, min}},
  1013. {start_mfa, {pooled_gs, start_link, [{foo_type}]}}],
  1014. application:set_env(pooler, pools, [Pool])
  1015. end,
  1016. fun(_) ->
  1017. application:unset_env(pooler, pools)
  1018. end,
  1019. [
  1020. {"default behavior kills the pool member",
  1021. fun() ->
  1022. ok = application:start(pooler),
  1023. Reason = monitor_members_trigger_culling_and_return_reason(),
  1024. ok = application:stop(pooler),
  1025. ?assertEqual(killed, Reason)
  1026. end},
  1027. {"custom callback terminates the pool member normally",
  1028. fun() ->
  1029. {ok, [Pool]} = application:get_env(pooler, pools),
  1030. Stop = {stop_mfa, {pooled_gs, stop, ['$pooler_pid']}},
  1031. application:set_env(pooler, pools, [[Stop | Pool]]),
  1032. ok = application:start(pooler),
  1033. Reason = monitor_members_trigger_culling_and_return_reason(),
  1034. ok = application:stop(pooler),
  1035. ?assertEqual(normal, Reason)
  1036. end}]}.
  1037. no_error_logger_reports_after_culling_test_() ->
  1038. %% damn those wraiths! This is the cure
  1039. {foreach,
  1040. fun() ->
  1041. {ok, _Pid} = error_logger_mon:start_link(),
  1042. Pool = [{name, test_pool_1},
  1043. {max_count, 3},
  1044. {init_count, 2},
  1045. {cull_interval, {200, ms}},
  1046. {max_age, {0, min}},
  1047. {start_mfa, {pooled_gs, start_link, [{type}]}}],
  1048. application:set_env(pooler, pools, [Pool])
  1049. end,
  1050. fun(_) ->
  1051. ok = error_logger_mon:stop(),
  1052. error_logger:delete_report_handler(error_logger_pooler_h),
  1053. application:unset_env(pooler, pools)
  1054. end,
  1055. [
  1056. {"Force supervisor to report by using exit/2 instead of terminate_child",
  1057. fun() ->
  1058. {ok, [Pool]} = application:get_env(pooler, pools),
  1059. Stop = {stop_mfa, {erlang, exit, ['$pooler_pid', kill]}},
  1060. application:set_env(pooler, pools, [[Stop | Pool]]),
  1061. ok = application:start(pooler),
  1062. error_logger:add_report_handler(error_logger_pooler_h),
  1063. Reason = monitor_members_trigger_culling_and_return_reason(),
  1064. %% we need to wait for the message to arrive before deleting handler
  1065. timer:sleep(250),
  1066. error_logger:delete_report_handler(error_logger_pooler_h),
  1067. ok = application:stop(pooler),
  1068. ?assertEqual(killed, Reason),
  1069. ?assertEqual(1, error_logger_mon:get_msg_count())
  1070. end},
  1071. {"Default MFA shouldn't generate any reports during culling",
  1072. fun() ->
  1073. ok = application:start(pooler),
  1074. error_logger:add_report_handler(error_logger_pooler_h),
  1075. Reason = monitor_members_trigger_culling_and_return_reason(),
  1076. error_logger:delete_report_handler(error_logger_pooler_h),
  1077. ok = application:stop(pooler),
  1078. ?assertEqual(killed, Reason),
  1079. ?assertEqual(0, error_logger_mon:get_msg_count())
  1080. end}
  1081. ]}.
  1082. monitor_members_trigger_culling_and_return_reason() ->
  1083. Pids = get_n_pids(test_pool_1, 3, []),
  1084. [ erlang:monitor(process, P) || P <- Pids ],
  1085. [ pooler:return_member(test_pool_1, P) || P <- Pids ],
  1086. receive
  1087. {'DOWN', _Ref, process, _Pid, Reason} ->
  1088. Reason
  1089. after
  1090. 250 -> timeout
  1091. end.
  1092. time_as_millis_test_() ->
  1093. Zeros = [ {{0, U}, 0} || U <- [min, sec, ms, mu] ],
  1094. Ones = [{{1, min}, 60000},
  1095. {{1, sec}, 1000},
  1096. {{1, ms}, 1},
  1097. {{1, mu}, 0}],
  1098. Misc = [{{3000, mu}, 3}],
  1099. Tests = Zeros ++ Ones ++ Misc,
  1100. [ ?_assertEqual(E, pooler:time_as_millis(I)) || {I, E} <- Tests ].
  1101. time_as_micros_test_() ->
  1102. Zeros = [ {{0, U}, 0} || U <- [min, sec, ms, mu] ],
  1103. Ones = [{{1, min}, 60000000},
  1104. {{1, sec}, 1000000},
  1105. {{1, ms}, 1000},
  1106. {{1, mu}, 1}],
  1107. Misc = [{{3000, mu}, 3000}],
  1108. Tests = Zeros ++ Ones ++ Misc,
  1109. [ ?_assertEqual(E, pooler:time_as_micros(I)) || {I, E} <- Tests ].
  1110. call_free_members_test_() ->
  1111. NumWorkers = 10,
  1112. PoolName = test_pool_1,
  1113. {setup,
  1114. fun() ->
  1115. application:set_env(pooler, metrics_module, fake_metrics),
  1116. fake_metrics:start_link()
  1117. end,
  1118. fun(_X) ->
  1119. fake_metrics:stop()
  1120. end,
  1121. {foreach,
  1122. fun() ->
  1123. Pool = [{name, PoolName},
  1124. {max_count, NumWorkers},
  1125. {init_count, NumWorkers},
  1126. {start_mfa,
  1127. {pooled_gs, start_link, [{"type-0"}]}}],
  1128. application:unset_env(pooler, pools),
  1129. application:start(pooler),
  1130. pooler:new_pool(Pool)
  1131. end,
  1132. fun(_X) ->
  1133. application:stop(pooler)
  1134. end,
  1135. [
  1136. {"perform a ping across the pool when all workers are free",
  1137. fun() ->
  1138. ?assertEqual(NumWorkers, (dump_pool(PoolName))#pool.free_count),
  1139. Res = pooler:call_free_members(PoolName, fun pooled_gs:ping/1),
  1140. ?assertEqual(NumWorkers, count_pongs(Res))
  1141. end},
  1142. {"perform a ping across the pool when two workers are taken",
  1143. fun() ->
  1144. ?assertEqual(NumWorkers, (dump_pool(PoolName))#pool.free_count),
  1145. Pids = [pooler:take_member(PoolName) || _X <- lists:seq(0, 1)],
  1146. Res = pooler:call_free_members(PoolName, fun pooled_gs:ping/1),
  1147. ?assertEqual(NumWorkers -2, count_pongs(Res)),
  1148. [pooler:return_member(PoolName, P) || P <- Pids]
  1149. end},
  1150. {"perform an op where the op crashes all free members",
  1151. fun() ->
  1152. ?assertEqual(NumWorkers, (dump_pool(PoolName))#pool.free_count),
  1153. Res = pooler:call_free_members(PoolName,
  1154. fun pooled_gs:error_on_call/1),
  1155. ?assertEqual(NumWorkers, count_errors(Res))
  1156. end}
  1157. ]}}.
  1158. count_pongs(Result) ->
  1159. lists:foldl(fun({ok, pong}, Acc) -> Acc + 1;
  1160. ({error, _}, Acc) -> Acc
  1161. end,
  1162. 0,
  1163. Result).
  1164. count_errors(Result) ->
  1165. lists:foldl(fun({error, _}, Acc) -> Acc + 1;
  1166. ({ok, _}, Acc) -> Acc
  1167. end,
  1168. 0,
  1169. Result).
  1170. % testing crash recovery means race conditions when either pids
  1171. % haven't yet crashed or pooler hasn't recovered. So this helper loops
  1172. % forver until N pids are obtained, ignoring error_no_members.
  1173. get_n_pids(N, Acc) ->
  1174. get_n_pids(test_pool_1, N, Acc).
  1175. get_n_pids(_Pool, 0, Acc) ->
  1176. Acc;
  1177. get_n_pids(Pool, N, Acc) ->
  1178. case pooler:take_member(Pool) of
  1179. error_no_members ->
  1180. get_n_pids(Pool, N, Acc);
  1181. Pid ->
  1182. get_n_pids(Pool, N - 1, [Pid|Acc])
  1183. end.
  1184. get_n_pids_group(_Group, 0, Acc) ->
  1185. Acc;
  1186. get_n_pids_group(Group, N, Acc) ->
  1187. case pooler:take_group_member(Group) of
  1188. error_no_members ->
  1189. get_n_pids_group(Group, N, Acc);
  1190. Pid ->
  1191. get_n_pids_group(Group, N - 1, [Pid|Acc])
  1192. end.
  1193. children_count(SupId) ->
  1194. length(supervisor:which_children(SupId)).
  1195. starting_members(PoolName) ->
  1196. length((dump_pool(PoolName))#pool.starting_members).
  1197. dump_pool(PoolName) ->
  1198. gen_server:call(PoolName, dump_pool).