Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easier non-DB-dependent example && comments in between #101

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ ok

## Example

This is an example application showcasing database connection pools using
Poolboy and [epgsql](https://github.com/epgsql/epgsql).
This is a very simple Poolboy example showcasing the call flow. Print statements at various checkpoints and dummy call of workers (doing nothing actually) is what we aim to demonstrate in this example.

### example.app

Expand All @@ -38,20 +37,20 @@ Poolboy and [epgsql](https://github.com/epgsql/epgsql).
{pool1, [
{size, 10},
{max_overflow, 20}
], [
{hostname, "127.0.0.1"},
{database, "db1"},
{username, "db1"},
{password, "abc123"}
], [
{hostname, "127.0.0.1/login.php"},
{database, "database"},
{username, "root"},
{password, "rootpassword"}
]},
{pool2, [
{size, 5},
{max_overflow, 10}
], [
{hostname, "127.0.0.1"},
{database, "db2"},
{username, "db2"},
{password, "abc123"}
], [
{hostname, "127.0.0.1/login.php"},
{database, "database"},
{username, "root"},
{password, "rootpassword"}
]}
]}
]}
Expand All @@ -65,40 +64,55 @@ Poolboy and [epgsql](https://github.com/epgsql/epgsql).
-behaviour(application).
-behaviour(supervisor).

-export([start/0, stop/0, squery/2, equery/3]).
-export([start/0, stop/0, squery/2, equery/3, xquery/3]).
-export([start/2, stop/1]).
-export([init/1]).

start() ->
io:fwrite("#MESSAGE : ~p calls example:start()~n", [self()]),
application:start(?MODULE).

stop() ->
io:fwrite("#MESSAGE : ~p calls example:stop()~n", [self()]),
application:stop(?MODULE).

start(_Type, _Args) ->
io:fwrite("#MESSAGE : ~p calls supervisor:start_link()~n(default callback is example:init())~n", [self()]),
supervisor:start_link({local, example_sup}, ?MODULE, []).

stop(_State) ->
ok.

init([]) ->
io:fwrite("#MESSAGE : ~p called example:init()~n", [self()]),
{ok, Pools} = application:get_env(example, pools),
io:fwrite("#MESSAGE : ~p defining PoolSpecs~n", [self()]),
PoolSpecs = lists:map(fun({Name, SizeArgs, WorkerArgs}) ->
io:fwrite("#MESSAGE : ~p defining PoolArgs~n", [self()]),
PoolArgs = [{name, {local, Name}},
{worker_module, example_worker}] ++ SizeArgs,
poolboy:child_spec(Name, PoolArgs, WorkerArgs)
end, Pools),
io:fwrite("#MESSAGE : ~p example:init() call is now complete~n", [self()]),
{ok, {{one_for_one, 10, 10}, PoolSpecs}}.

squery(PoolName, Sql) ->
io:fwrite("i called squery call~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {squery, Sql})
end).

equery(PoolName, Stmt, Params) ->
io:fwrite("i called equery call~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {equery, Stmt, Params})
end).

xquery(PoolName, Stmt, Params) ->
io:fwrite("i called xquery cast~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:cast(Worker, {xquery, Stmt, Params})
end).
```

### example_worker.erl
Expand All @@ -111,43 +125,69 @@ equery(PoolName, Stmt, Params) ->
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-export([warmup /0]).

-record(state, {conn}).

start_link(Args) ->
gen_server:start_link(?MODULE, Args, []).

init(Args) ->
io:fwrite("#MESSAGE : ~p defines a new worker~n", [self()]),
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}}.
io:fwrite("#MESSAGE : ~p will be starting to warmup~n", [self()]),
{ok} = warmup(),
io:fwrite("#MESSAGE : ~p warmup is complete~n~n~n", [self()]),
{ok, ok}.

warmup() ->
io:fwrite("#MESSAGE : ~p is now warming up~n", [self()]),
{ok}.

handle_call({squery, Sql}, _From, #state{conn=Conn}=State) ->
io:fwrite("lets handle a squery call~n"),
{reply, epgsql:squery(Conn, Sql), State};
handle_call({equery, Stmt, Params}, _From, #state{conn=Conn}=State) ->
io:fwrite("lets handle a equery call~n"),
{reply, epgsql:equery(Conn, Stmt, Params), State};
handle_call(_Request, _From, State) ->
io:fwrite("call handling success~n"),
{reply, ok, State}.

handle_cast(_Msg, State) ->
io:fwrite("lets handle a cast (async call)~n"),
{noreply, State}.

handle_info(_Info, State) ->
io:fwrite("lets example_worker:handle_info~n"),
{noreply, State}.

terminate(_Reason, #state{conn=Conn}) ->
ok = epgsql:close(Conn),
terminate(_Reason, ok) ->
io:fwrite("#MESSAGE : ~p wants to terminate the worker~n", [self()]),
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.
```
## Example usage

```erl-sh
1> example:start().
--- % start the supervisors, initiate the workers
2> example:squery(pool1, hello).
--- % get the workers to work : (synchronous) call
3> example:equery(pool1, hello, world).
--- % get the workers to work : (synchronous) call
4> example:xquery(pool1, hello, world).
--- % get the workers to work : (asynchronous) cast
5> example:stop().
--- % kill the workers and end the pool
```
Find the complete terminal screenshots along with stats and explanation [here](https://github.com/rounakdatta/poolboy/edit/master/report.pdf).

## Options

Expand Down
30 changes: 30 additions & 0 deletions example/example.app
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{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},
{max_overflow, 20}
], [
{hostname, "127.0.0.1"},
{database, "db1"},
{username, "db1"},
{password, "abc123"}
]},
{pool2, [
{size, 5},
{max_overflow, 10}
], [
{hostname, "127.0.0.1"},
{database, "db2"},
{username, "db2"},
{password, "abc123"}
]}
]}
]}
]}.
53 changes: 53 additions & 0 deletions example/example.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-module(example).
-behaviour(application).
-behaviour(supervisor).

-export([start/0, stop/0, squery/2, equery/3, xquery/3]).
-export([start/2, stop/1]).
-export([init/1]).

start() ->
io:fwrite("#MESSAGE : ~p calls example:start()~n", [self()]),
application:start(?MODULE).

stop() ->
io:fwrite("#MESSAGE : ~p calls example:stop()~n", [self()]),
application:stop(?MODULE).

start(_Type, _Args) ->
io:fwrite("#MESSAGE : ~p calls supervisor:start_link()~n(default callback is example:init())~n", [self()]),
supervisor:start_link({local, example_sup}, ?MODULE, []).

stop(_State) ->
ok.

init([]) ->
io:fwrite("#MESSAGE : ~p called example:init()~n", [self()]),
{ok, Pools} = application:get_env(example, pools),
io:fwrite("#MESSAGE : ~p defining PoolSpecs~n", [self()]),
PoolSpecs = lists:map(fun({Name, SizeArgs, WorkerArgs}) ->
io:fwrite("#MESSAGE : ~p defining PoolArgs~n", [self()]),
PoolArgs = [{name, {local, Name}},
{worker_module, example_worker}] ++ SizeArgs,
poolboy:child_spec(Name, PoolArgs, WorkerArgs)
end, Pools),
io:fwrite("#MESSAGE : ~p example:init() call is now complete~n", [self()]),
{ok, {{one_for_one, 10, 10}, PoolSpecs}}.

squery(PoolName, Sql) ->
io:fwrite("i called squery call~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {squery, Sql})
end).

equery(PoolName, Stmt, Params) ->
io:fwrite("i called equery call~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:call(Worker, {equery, Stmt, Params})
end).

xquery(PoolName, Stmt, Params) ->
io:fwrite("i called xquery cast~n"),
poolboy:transaction(PoolName, fun(Worker) ->
gen_server:cast(Worker, {xquery, Stmt, Params})
end).
55 changes: 55 additions & 0 deletions example/example_worker.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-module(example_worker).
-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]).
-export([warmup /0]).

-record(state, {conn}).

start_link(Args) ->
%io:fwrite("#MESSAGE : ~p calls example_worker:start_link()~n(default callback is example_worker:init())~n"),
gen_server:start_link(?MODULE, Args, []).

init(Args) ->
io:fwrite("#MESSAGE : ~p defines a new worker~n", [self()]),
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),
io:fwrite("#MESSAGE : ~p will be starting to warmup~n", [self()]),
{ok} = warmup(),
io:fwrite("#MESSAGE : ~p warmup is complete~n~n~n", [self()]),
{ok, ok}.

warmup() ->
io:fwrite("#MESSAGE : ~p is now warming up~n", [self()]),
{ok}.

handle_call({squery, Sql}, _From, #state{conn=Conn}=State) ->
io:fwrite("lets handle a squery call~n"),
{reply, epgsql:squery(Conn, Sql), State};
handle_call({equery, Stmt, Params}, _From, #state{conn=Conn}=State) ->
io:fwrite("lets handle a equery call~n"),
{reply, epgsql:equery(Conn, Stmt, Params), State};
handle_call(_Request, _From, State) ->
io:fwrite("call handling success~n"),
{reply, ok, State}.

handle_cast(_Msg, State) ->
io:fwrite("lets handle a cast (async call)~n"),
{noreply, State}.

handle_info(_Info, State) ->
io:fwrite("lets example_worker:handle_info~n"),
{noreply, State}.

terminate(_Reason, ok) ->
io:fwrite("#MESSAGE : ~p wants to terminate the worker~n", [self()]),
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Binary file added report.pdf
Binary file not shown.
1 change: 1 addition & 0 deletions src/poolboy.erl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ status(Pool) ->

init({PoolArgs, WorkerArgs}) ->
process_flag(trap_exit, true),
io:fwrite("#MESSAGE : ~p starting poolboy server~n", [self()]),
Waiting = queue:new(),
Monitors = ets:new(monitors, [private]),
init(PoolArgs, WorkerArgs, #state{waiting = Waiting, monitors = Monitors}).
Expand Down
1 change: 1 addition & 0 deletions src/poolboy_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ start_link(Mod, Args) ->
supervisor:start_link(?MODULE, {Mod, Args}).

init({Mod, Args}) ->
io:fwrite("#MESSAGE : ~p starting poolboy supervisor~n", [self()]),
{ok, {{simple_one_for_one, 0, 1},
[{Mod, {Mod, start_link, [Args]},
temporary, 5000, worker, [Mod]}]}}.