diff --git a/README.md b/README.md index e603768..7155a4b 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,11 @@ code_change(_OldVsn, State, _Extra) -> ## Options - `name`: the pool name -- `worker_module`: the module that represents the workers -- `size`: maximum pool size +- `worker_module`: the module that represents the workers, if the workers are created + with dynamic arguments (each one of them) then you must pass a list and each element + of the list is a new worker. example: `{worker_module, {worker, [[1], [2]]}}`. +- `size`: maximum pool size, if you use dynamical workers then the size is detected from + list arguments length - `max_overflow`: maximum number of workers created if pool is empty - `strategy`: `lifo` or `fifo`, determines whether checked in workers should be placed first or last in the line of available workers. Default is `lifo`. diff --git a/src/poolboy.erl b/src/poolboy.erl index db4973b..464ce5d 100644 --- a/src/poolboy.erl +++ b/src/poolboy.erl @@ -126,10 +126,22 @@ init({PoolArgs, WorkerArgs}) -> process_flag(trap_exit, true), Waiting = queue:new(), Monitors = ets:new(monitors, [private]), - init(PoolArgs, WorkerArgs, #state{waiting = Waiting, monitors = Monitors}). - -init([{worker_module, Mod} | Rest], WorkerArgs, State) when is_atom(Mod) -> - {ok, Sup} = poolboy_sup:start_link(Mod, WorkerArgs), + % check if workers has different args + Size = case proplists:get_value(worker_module, PoolArgs) of + {_Mod, WorkerDynArgs} when is_list(WorkerDynArgs) -> + [{size, length(WorkerDynArgs)}]; + _Mod -> + [] + end, + init(PoolArgs ++ Size, WorkerArgs, #state{waiting = Waiting, monitors = Monitors}). + +init([{worker_module, {Mod, []}} | Rest], WorkerArgs, State) when is_atom(Mod) -> + init(Rest, WorkerArgs, State); +init([{worker_module, {Mod, WorkerDynArgs}} | Rest], _WorkerArgs, State) when is_atom(Mod) -> + {ok, Sup} = poolboy_sup:start_link(Mod, {dynamic, WorkerDynArgs}), + init(Rest, _WorkerArgs, State#state{supervisor = {Sup, {Mod, WorkerDynArgs}}}); +init([{worker_module, Mod} | Rest], WorkerArgs, State) when is_atom(Mod) -> + {ok, Sup} = poolboy_sup:start_link(Mod, WorkerArgs), init(Rest, WorkerArgs, State#state{supervisor = Sup}); init([{size, Size} | Rest], WorkerArgs, State) when is_integer(Size) -> init(Rest, WorkerArgs, State#state{size = Size}); @@ -257,7 +269,12 @@ handle_info(_Info, State) -> terminate(_Reason, State) -> ok = lists:foreach(fun (W) -> unlink(W) end, State#state.workers), - true = exit(State#state.supervisor, shutdown), + true = case State#state.supervisor of + {Sup, {_Mod, _WorkerDynArgs}} -> + exit(Sup, shutdown); + Sup -> + exit(Sup, shutdown) + end, ok. code_change(_OldVsn, State, _Extra) -> @@ -271,6 +288,11 @@ start_pool(StartFun, PoolArgs, WorkerArgs) -> gen_server:StartFun(Name, ?MODULE, {PoolArgs, WorkerArgs}, []) end. +new_worker({N, Sup, Mod, Args}) -> + {ok, Pid} = supervisor:start_child(Sup, {N, {Mod, start_link, [Args]}, + temporary,5000,worker,[Mod]}), + true = link(Pid), + Pid; new_worker(Sup) -> {ok, Pid} = supervisor:start_child(Sup, []), true = link(Pid), @@ -292,6 +314,8 @@ prepopulate(N, Sup) -> prepopulate(0, _Sup, Workers) -> Workers; +prepopulate(N, {Sup, {Mod, [ Args | Rest ]}}, Workers) -> + prepopulate(N-1, {Sup, {Mod, Rest}}, [new_worker({N, Sup, Mod, Args}) | Workers]); prepopulate(N, Sup, Workers) -> prepopulate(N-1, Sup, [new_worker(Sup) | Workers]). diff --git a/src/poolboy_sup.erl b/src/poolboy_sup.erl index e6485a6..ac0e749 100644 --- a/src/poolboy_sup.erl +++ b/src/poolboy_sup.erl @@ -8,7 +8,11 @@ start_link(Mod, Args) -> supervisor:start_link(?MODULE, {Mod, Args}). -init({Mod, Args}) -> +init({_Mod, {dynamic, _Args}}) -> + % arguments are dynamic for each worker, then each worker must + % be created dynamically + {ok, { {one_for_one, 0, 1}, []} }; +init({Mod, Args}) -> {ok, {{simple_one_for_one, 0, 1}, [{Mod, {Mod, start_link, [Args]}, temporary, 5000, worker, [Mod]}]}}. diff --git a/test/poolboy_test_worker_dyn.erl b/test/poolboy_test_worker_dyn.erl new file mode 100644 index 0000000..3383425 --- /dev/null +++ b/test/poolboy_test_worker_dyn.erl @@ -0,0 +1,32 @@ +-module(poolboy_test_worker_dyn). +-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]). + +start_link(Args) -> + gen_server:start_link(?MODULE, [Args], []). + +init([[N]]) -> + {ok, N}. + +handle_call(die, _From, State) -> + {stop, {error, died}, dead, State}; +handle_call(get_info, _From, State) -> + {reply, {ok, State}, State}; +handle_call(_Event, _From, State) -> + {reply, ok, State}. + +handle_cast(_Event, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/test/poolboy_tests.erl b/test/poolboy_tests.erl index b0f3b39..0d6040d 100644 --- a/test/poolboy_tests.erl +++ b/test/poolboy_tests.erl @@ -72,7 +72,10 @@ pool_test_() -> {<<"Recover from timeout without exit handling">>, fun transaction_timeout_without_exit/0}, {<<"Recover from transaction timeout">>, - fun transaction_timeout/0} + fun transaction_timeout/0}, + {<<"Pool with workers using different arguments">>, + fun pool_dyn_worker_args/0 + } ] }. @@ -92,6 +95,27 @@ checkin_worker(Pid, Worker) -> poolboy:checkin(Pid, Worker), timer:sleep(500). +pool_dyn_worker_args() -> + %% Check pool operation when arguments for each worker is dynamic. + %% Size of workers is decided by length of args. + Args = [ [N] || N <- lists:seq(1, 10) ], + {ok, Pid} = new_dyn_pool(Args, 5, lifo), + ?assertEqual(10, length(pool_call(Pid, get_avail_workers))), + Worker0 = poolboy:checkout(Pid), + %% Check if state of worker is the same of args. + {ok, State0} = gen_server:call(Worker0, get_info), + ?assertEqual(10, State0), + ?assertEqual(9, length(pool_call(Pid, get_avail_workers))), + Worker = poolboy:checkout(Pid), + % Check again if state of worker is the same of args. + {ok, State} = gen_server:call(Worker, get_info), + ?assertEqual(9, State), + ?assertEqual(8, length(pool_call(Pid, get_avail_workers))), + checkin_worker(Pid, Worker), + ?assertEqual(9, length(pool_call(Pid, get_avail_workers))), + ?assertEqual(1, length(pool_call(Pid, get_all_monitors))), + + ok = pool_call(Pid, stop). transaction_timeout_without_exit() -> {ok, Pid} = new_pool(1, 0), @@ -546,5 +570,10 @@ new_pool(Size, MaxOverflow, Strategy) -> {size, Size}, {max_overflow, MaxOverflow}, {strategy, Strategy}]). +new_dyn_pool(Args, MaxOverflow, Strategy) -> + poolboy:start_link([{name, {local, poolboy_test}}, + {worker_module, {poolboy_test_worker_dyn, Args}}, + {max_overflow, MaxOverflow}, {strategy, Strategy}]). + pool_call(ServerRef, Request) -> gen_server:call(ServerRef, Request).