Знайомство з Erlang [16]
epgsql_pool transactions
розглянемо роботу з postgresql-транзакціями при користуванні
epgsql та epgsql_pool на прикладі https://github.com/221V/eauc
обгортки звичайних запитів виглядають так:
select(Q,A) ->
case epgsql_pool:query(my_main_pool, Q, A) of
{ok,_,R} ->
R;
{error,E} ->
io:format("~p~n", [E]),
{error,E}
end.
in_up_del(Q,A) ->
case epgsql_pool:query(my_main_pool, Q, A) of
{ok,R} ->
R;
{error,E} ->
io:format("~p~n", [E]),
{error,E}
end.
returning(Q,A) ->
case epgsql_pool:query(my_main_pool, Q, A) of
{ok,1,_,R} ->
R;
{error,E} ->
io:format("~p~n", [E]),
{error,E}
end.
при підключенні epgsql_pool ми створюємо нову обгортку для транзакцій,
і відповідно, обгортку для запитів, виконуваних в транзакції
( транзакція вимагає явно вказувати процес пула )
transaction(Fun) ->
case epgsql_pool:transaction(my_main_pool, Fun) of
{ok, _} ->
ok;
Error ->
io:format("transaction error: ~p~n in tr fun: ~p~n", [Error, Fun]),
Error
end.
transaction_q(Worker, Q, A) ->
epgsql_pool:query(Worker, Q, A).
запит виглядає так:
make_new_bet(Worker, Lot_Id, Nickname, User_Id, Bet_Add, Bet_Total) ->
pg:transaction_q(Worker, "INSERT INTO eauc_bets (lot_id, nickname, uid, bet_add, bet_total) VALUES ($1, $2, $3, $4, $5)", [Lot_Id, Nickname, User_Id, Bet_Add, Bet_Total]).
транзакція виглядає так:
( як приклад, можна ще перевіряти результати окремих запитів всередині транзації )
ResultN = pg:transaction(fun(Worker) ->
%% transaction
pq:make_new_bet(Worker, Lot_Id, Nickname, User_Id, Bet, Bet),
pq:update_user_money(Worker, User_Id, Money_New),
pq:make_money_log(Worker, User_Id, User_Money, Bet, 1),
pq:update_lot_info(Worker, Lot_Id, Bet, Nickname, User_Id)
end),
case ResultN of
ok ->
%% transaction ok
...
;
_ ->
%% transaction err
...
end;
виключення і помилки всередині транзакції відміняють всі запити транзакції:
( приклад з readme https://github.com/wgnet/epgsql_pool )
epgsql_pool:transaction(my_pool,
fun(Worker) ->
Res1 = epgsql_pool:query(Worker, Query1),
...
case SomeData of
GoodResult -> do_something;
BadResult -> throw(cancel_transaction)
end,
ResN = epgsql_pool:query(Worker, QueryN)
end).
окремо замітка про налаштування - sys.config
{epgsql_pool,
[{connection_timeout, 5000},
{query_timeout, 3000},
{transaction_timeout, 5000} ]}
це різні таймаути, рекомендується залишати значення по-замовчуванню
і ще один момент -- транзакція повинна виконуватись якомога скоріше,
і бажано не виконувати http-запитів всередині транзакції :)
Посилання
https://github.com/wgnet/epgsql_pool
https://github.com/epgsql/epgsql