Skip to content

Commit 4e89f39

Browse files
committed
Merge pull request #7 from edhollandAL/dev
Add support for transactions and improved timeouts
2 parents 7c59760 + 3b6d19a commit 4e89f39

File tree

3 files changed

+144
-43
lines changed

3 files changed

+144
-43
lines changed

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,27 @@ Build and start the application with Make:
1414

1515
~:$ make
1616
~:$ make run
17-
17+
1818
- To test it, just run:
19-
19+
2020
> pgapp:equery(a_pool_name, "select current_date", []).
21-
21+
2222
where `a_pool_name` is the name of one of the pools in your `pgapp.config` file.
2323

2424
API use:
2525
- Simple pool:
26-
26+
2727
application:ensure_all_started(pgapp).
2828
pgapp:connect([{size, 10}, {database, "mydb"}, {username, "foo"}, {password, "bar"}]).
29-
pgapp:equery("select current_date", []).
30-
29+
pgapp:equery("select current_date", []),
30+
pgapp:with_transaction(fun() ->
31+
pgapp:squery("update ..."),
32+
pgapp:squery("delete from ..."),
33+
pgapp:equery("select ? from ?", ["*", Table])
34+
end).
35+
3136
- Multi pool:
32-
37+
3338
application:ensure_all_started(pgapp).
3439
pgapp:connect(a_pool_name, [{size, 10}, {database, "mydb"}, {username, "foo"}, {password, "bar"}]).
3540
pgapp:equery(a_pool_name, "select current_date", []).

src/pgapp.erl

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
-module(pgapp).
1010

1111
%% API
12-
-export([connect/1, connect/2, equery/2, equery/3, squery/1, squery/2]).
12+
-export([connect/1, connect/2,
13+
equery/2, equery/3, equery/4,
14+
squery/1, squery/2, squery/3,
15+
with_transaction/1, with_transaction/2, with_transaction/3]).
1316

1417
%%%===================================================================
1518
%%% API
@@ -19,40 +22,72 @@ connect(Settings) ->
1922
connect(epgsql_pool, Settings).
2023

2124
connect(PoolName, Settings) ->
22-
PoolSize = proplists:get_value(size, Settings, 5),
25+
PoolSize = proplists:get_value(size, Settings, 5),
2326
MaxOverflow = proplists:get_value(max_overflow, Settings, 5),
24-
pgapp_sup:add_pool(PoolName,
25-
[{name, {local, PoolName}},
26-
{worker_module, pgapp_worker},
27-
{size, PoolSize},
28-
{max_overflow, MaxOverflow}],
29-
Settings).
30-
31-
-spec equery(Sql::epgsql:sql_query(),
32-
Params :: list(epgsql:bind_param())) -> epgsql:reply(epgsql:equery_row()).
27+
pgapp_sup:add_pool(PoolName, [{name, {local, PoolName}},
28+
{worker_module, pgapp_worker},
29+
{size, PoolSize},
30+
{max_overflow, MaxOverflow}], Settings).
31+
32+
-spec equery(Sql :: epgsql:sql_query(),
33+
Params :: list(epgsql:bind_param()))
34+
-> epgsql:reply(epgsql:equery_row()).
3335
equery(Sql, Params) ->
34-
equery(epgsql_pool, Sql, Params).
35-
36-
-spec equery(PoolName::atom(), Sql::epgsql:sql_query(),
37-
Params :: list(epgsql:bind_param())) -> epgsql:reply(epgsql:equery_row()).
38-
equery(PoolName, Sql, Params) ->
39-
poolboy:transaction(PoolName,
40-
fun(Worker) ->
41-
gen_server:call(Worker, {equery, Sql, Params})
42-
end).
43-
44-
-spec squery(Sql::epgsql:sql_query()) -> epgsql:reply(epgsql:squery_row()) |
45-
[epgsql:reply(epgsql:squery_row())].
36+
pgapp_worker:equery(Sql, Params).
37+
38+
-spec equery(Sql :: epgsql:sql_query(),
39+
Params :: list(epgsql:bind_param()),
40+
Timeout :: atom() | integer())
41+
-> epgsql:reply(epgsql:equery_row()).
42+
equery(Sql, Params, Timeout) ->
43+
pgapp_worker:equery(Sql, Params, Timeout).
44+
45+
46+
-spec equery(PoolName :: atom(), Sql::epgsql:sql_query(),
47+
Params :: list(epgsql:bind_param()),
48+
Timeout :: atom() | integer())
49+
-> epgsql:reply(epgsql:equery_row()).
50+
equery(PoolName, Sql, Params, Timeout) ->
51+
pgapp_worker:equery(PoolName, Sql, Params, Timeout).
52+
53+
-spec squery(Sql :: epgsql:sql_query())
54+
-> epgsql:reply(epgsql:squery_row()) |
55+
[epgsql:reply(epgsql:squery_row())].
4656
squery(Sql) ->
47-
squery(epgsql_pool, Sql).
48-
49-
-spec squery(PoolName::atom(), Sql::epgsql:sql_query()) -> epgsql:reply(epgsql:squery_row()) |
50-
[epgsql:reply(epgsql:squery_row())].
51-
squery(PoolName, Sql) ->
52-
poolboy:transaction(PoolName,
53-
fun(Worker) ->
54-
gen_server:call(Worker, {squery, Sql})
55-
end).
57+
pgapp_worker:squery(Sql).
58+
59+
-spec squery(Sql::epgsql:sql_query(),
60+
Timeout :: atom() | integer())
61+
-> epgsql:reply(epgsql:squery_row()) |
62+
[epgsql:reply(epgsql:squery_row())].
63+
squery(Sql, Timeout) ->
64+
pgapp_worker:squery(Sql, Timeout).
65+
66+
-spec squery(PoolName :: atom(),
67+
Sql :: epgsql:sql_query(),
68+
Timeout :: atom() | integer())
69+
-> epgsql:reply(epgsql:squery_row()) |
70+
[epgsql:reply(epgsql:squery_row())].
71+
squery(PoolName, Sql, Timeout) ->
72+
pgapp_worker:squery(PoolName, Sql, Timeout).
73+
74+
-spec with_transaction(Function :: fun(() -> Reply))
75+
-> Reply | {rollback, any()} when Reply :: any().
76+
with_transaction(Fun) when is_function(Fun, 0) ->
77+
with_transaction(epgsql_pool, Fun).
78+
79+
-spec with_transaction(PoolName :: atom(),
80+
Function :: fun(() -> Reply))
81+
-> Reply | {rollback, any()} when Reply :: any().
82+
with_transaction(PoolName, Fun) when is_function(Fun, 0) ->
83+
pgapp_worker:with_transaction(PoolName, Fun).
84+
85+
-spec with_transaction(PoolName :: atom(),
86+
Function :: fun(() -> Reply),
87+
Timeout :: atom() | non_neg_integer())
88+
-> Reply | {rollback, any()} when Reply :: any().
89+
with_transaction(PoolName, Fun, Timeout) when is_function(Fun, 0) ->
90+
pgapp_worker:with_transaction(PoolName, Fun, Timeout).
5691

5792
%%--------------------------------------------------------------------
5893
%% @doc

src/pgapp_worker.erl

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
-behaviour(gen_server).
99
-behaviour(poolboy_worker).
1010

11+
-export([squery/1, squery/2, squery/3,
12+
equery/2, equery/3, equery/4,
13+
with_transaction/2, with_transaction/3]).
14+
1115
-export([start_link/1]).
1216

1317
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -20,6 +24,55 @@
2024

2125
-define(INITIAL_DELAY, 500). % Half a second
2226
-define(MAXIMUM_DELAY, 5 * 60 * 1000). % Five minutes
27+
-define(TIMEOUT, 5 * 1000).
28+
29+
-define(STATE_VAR, '$pgapp_state').
30+
31+
squery(Sql) ->
32+
case get(?STATE_VAR) of
33+
undefined ->
34+
squery(epgsql_pool, Sql);
35+
Conn ->
36+
epgsql:squery(Conn, Sql)
37+
end.
38+
39+
squery(PoolName, Sql) ->
40+
squery(PoolName, Sql, ?TIMEOUT).
41+
42+
squery(PoolName, Sql, Timeout) ->
43+
poolboy:transaction(PoolName,
44+
fun (Worker) ->
45+
gen_server:call(Worker, {squery, Sql}, Timeout)
46+
end, Timeout).
47+
48+
49+
equery(Sql, Params) ->
50+
case get(?STATE_VAR) of
51+
undefined ->
52+
equery(epgsql_pool, Sql, Params);
53+
Conn ->
54+
epgsql:equery(Conn, Sql, Params)
55+
end.
56+
57+
equery(PoolName, Sql, Params) ->
58+
equery(PoolName, Sql, Params, ?TIMEOUT).
59+
60+
equery(PoolName, Sql, Params, Timeout) ->
61+
poolboy:transaction(PoolName,
62+
fun (Worker) ->
63+
gen_server:call(Worker,
64+
{equery, Sql, Params}, Timeout)
65+
end, Timeout).
66+
67+
with_transaction(PoolName, Fun) ->
68+
with_transaction(PoolName, Fun, ?TIMEOUT).
69+
70+
with_transaction(PoolName, Fun, Timeout) ->
71+
poolboy:transaction(PoolName,
72+
fun (Worker) ->
73+
gen_server:call(Worker,
74+
{transaction, Fun}, Timeout)
75+
end, Timeout).
2376

2477
start_link(Args) ->
2578
gen_server:start_link(?MODULE, Args, []).
@@ -28,11 +81,19 @@ init(Args) ->
2881
process_flag(trap_exit, true),
2982
{ok, connect(#state{start_args = Args, delay = ?INITIAL_DELAY})}.
3083

31-
handle_call({squery, Sql}, _From, #state{conn=Conn} = State) when Conn /= undefined ->
84+
handle_call({squery, Sql}, _From,
85+
#state{conn=Conn} = State) when Conn /= undefined ->
3286
{reply, epgsql:squery(Conn, Sql), State};
33-
34-
handle_call({equery, Sql, Params}, _From, #state{conn = Conn} = State) when Conn /= undefined ->
35-
{reply, epgsql:equery(Conn, Sql, Params), State}.
87+
handle_call({equery, Sql, Params}, _From,
88+
#state{conn = Conn} = State) when Conn /= undefined ->
89+
{reply, epgsql:equery(Conn, Sql, Params), State};
90+
91+
handle_call({transaction, Fun}, _From,
92+
#state{conn = Conn} = State) when Conn /= undefined ->
93+
put(?STATE_VAR, Conn),
94+
Result = epgsql:with_transaction(Conn, fun(_) -> Fun() end),
95+
erase(?STATE_VAR),
96+
{reply, Result, State}.
3697

3798
handle_cast(reconnect, State) ->
3899
{noreply, connect(State)}.

0 commit comments

Comments
 (0)