Skip to content

Commit ab041a1

Browse files
authored
Introduce begin, rollback and commit (#88)
* Add begin(!)/1,2 to checkout and begin * Add rollback(!)/1,2 to rollback and checkin * Add commit(!)/1,2 to commit (or rollback) and checkin rollback/2 is overloaded and has old behaviour inside transaction/3 for backwards compatibility. However transaction/3 is deprecated because is makes assumptions about database status that may not hold. New return values from handle_begin/2, handle_rollback/2 and handle_commit/2 allow to signal mistaken database status.
1 parent 48dc03f commit ab041a1

File tree

4 files changed

+673
-135
lines changed

4 files changed

+673
-135
lines changed

integration_test/cases/transaction_execute_test.exs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ defmodule TransactionExecuteTest do
263263
P.rollback(conn2, :oops)
264264
end) == {:error, :oops}
265265

266-
assert_raise DBConnection.ConnectionError, "transaction rolling back",
266+
assert_raise DBConnection.ConnectionError,
267+
"legacy transaction rolling back",
267268
fn() -> P.execute!(conn, %Q{}, [:param]) end
268269
end) == {:error, :rollback}
269270

@@ -273,7 +274,7 @@ defmodule TransactionExecuteTest do
273274
handle_rollback: [_, :new_state]] = A.record(agent)
274275
end
275276

276-
test "transaction does not log commit if closed" do
277+
test "transaction logs rollback if closed" do
277278
stack = [
278279
{:ok, :state},
279280
{:ok, :began, :new_state},
@@ -290,13 +291,12 @@ defmodule TransactionExecuteTest do
290291
log = &send(parent, &1)
291292

292293
P.transaction(pool, fn(conn) ->
293-
assert_received %DBConnection.LogEntry{call: :transaction,
294-
query: :begin}
294+
assert_received %DBConnection.LogEntry{call: :begin, query: :begin}
295295
assert_raise DBConnection.ConnectionError,
296296
fn() -> P.execute(conn, %Q{}, [:param]) end
297297
end, [log: log])
298298

299-
refute_received %DBConnection.LogEntry{call: :transaction}
299+
assert_received %DBConnection.LogEntry{call: :rollback, query: :rollback}
300300

301301
assert [
302302
{:connect, [_]},

integration_test/cases/transaction_test.exs

Lines changed: 198 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ defmodule TransactionTest do
5252
log = &send(parent, &1)
5353

5454
assert P.transaction(pool, fn(_) ->
55-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
56-
assert %{query: :begin, params: nil, result: {:ok, :began}} = entry
55+
assert_received %DBConnection.LogEntry{call: :begin} = entry
56+
assert %{query: :begin, params: nil, result: {:ok, _, :began}} = entry
5757
assert is_integer(entry.pool_time)
5858
assert entry.pool_time >= 0
5959
assert is_integer(entry.connection_time)
@@ -63,19 +63,19 @@ defmodule TransactionTest do
6363
:result
6464
end, [log: log]) == {:ok, :result}
6565

66-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
66+
assert_received %DBConnection.LogEntry{call: :commit} = entry
6767
assert %{query: :commit, params: nil, result: {:ok, :committed}} = entry
6868
assert is_nil(entry.pool_time)
6969
assert is_integer(entry.connection_time)
7070
assert entry.connection_time >= 0
7171
assert is_nil(entry.decode_time)
7272

7373
assert P.transaction(pool, fn(conn) ->
74-
assert_received %DBConnection.LogEntry{}
74+
assert_received %DBConnection.LogEntry{call: :begin}
7575
P.rollback(conn, :result)
7676
end, [log: log]) == {:error, :result}
7777

78-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
78+
assert_received %DBConnection.LogEntry{call: :rollback} = entry
7979
assert %{query: :rollback, params: nil, result: {:ok, :rolledback}} = entry
8080
assert is_nil(entry.pool_time)
8181
assert is_integer(entry.connection_time)
@@ -304,7 +304,7 @@ defmodule TransactionTest do
304304
P.transaction(pool, fn(_) -> flunk("transaction ran") end, [log: log])
305305
end
306306

307-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
307+
assert_received %DBConnection.LogEntry{call: :begin} = entry
308308
assert %{query: :begin, params: nil, result: {:error, ^err}} = entry
309309
assert is_integer(entry.pool_time)
310310
assert entry.pool_time >= 0
@@ -341,7 +341,7 @@ defmodule TransactionTest do
341341
P.transaction(pool, fn(_) -> flunk("transaction ran") end, [log: log])
342342
end
343343

344-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
344+
assert_received %DBConnection.LogEntry{call: :begin} = entry
345345
assert %{query: :begin, params: nil, result: {:error, err}} = entry
346346
assert %DBConnection.ConnectionError{message: "an exception was raised: ** (RuntimeError) oops" <> _} = err
347347
assert is_integer(entry.pool_time)
@@ -494,11 +494,11 @@ defmodule TransactionTest do
494494
assert_raise RuntimeError, "oops",
495495
fn() ->
496496
P.transaction(pool, fn(_) ->
497-
assert_received %DBConnection.LogEntry{}
497+
assert_received %DBConnection.LogEntry{call: :begin}
498498
end, [log: log])
499499
end
500500

501-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
501+
assert_received %DBConnection.LogEntry{call: :commit} = entry
502502
assert %{query: :commit, params: nil, result: {:error, ^err}} = entry
503503
assert is_nil(entry.pool_time)
504504
assert is_integer(entry.connection_time)
@@ -635,10 +635,10 @@ defmodule TransactionTest do
635635
P.transaction(pool, fn(_) -> :ok end, [log: log])
636636
end
637637

638-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
639-
assert %{query: :begin, params: nil, result: {:ok, :began}} = entry
638+
assert_received %DBConnection.LogEntry{call: :begin} = entry
639+
assert %{query: :begin, params: nil, result: {:ok, _, :began}} = entry
640640

641-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
641+
assert_received %DBConnection.LogEntry{call: :commit} = entry
642642
assert %{query: :commit, params: nil, result: {:error, err}} = entry
643643
assert %DBConnection.ConnectionError{message: "an exception was raised: ** (RuntimeError) oops" <> _} = err
644644
assert is_nil(entry.pool_time)
@@ -725,10 +725,10 @@ defmodule TransactionTest do
725725
P.transaction(pool, &P.rollback(&1, :oops), [log: log])
726726
end
727727

728-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
729-
assert %{query: :begin, params: nil, result: {:ok, :began}} = entry
728+
assert_received %DBConnection.LogEntry{call: :begin} = entry
729+
assert %{query: :begin, params: nil, result: {:ok, _, :began}} = entry
730730

731-
assert_received %DBConnection.LogEntry{call: :transaction} = entry
731+
assert_received %DBConnection.LogEntry{call: :rollback} = entry
732732
assert %{query: :rollback, params: nil, result: {:error, err}} = entry
733733
assert %DBConnection.ConnectionError{message: "an exception was raised: ** (RuntimeError) oops" <> _} = err
734734
assert is_nil(entry.pool_time)
@@ -760,17 +760,197 @@ defmodule TransactionTest do
760760
assert_raise RuntimeError, "oops",
761761
fn() ->
762762
P.transaction(pool, fn(_) ->
763-
assert_received %DBConnection.LogEntry{call: :transaction,
764-
query: :begin}
763+
assert_received %DBConnection.LogEntry{call: :begin, query: :begin}
765764
raise "oops"
766765
end, [log: log])
767766
end
768767

769-
assert_received %DBConnection.LogEntry{call: :transaction, query: :rollback}
768+
assert_received %DBConnection.LogEntry{call: :rollback, query: :rollback}
770769

771770
assert [
772771
connect: [_],
773772
handle_begin: [_, :state],
774773
handle_rollback: [_, :new_state]] = A.record(agent)
775774
end
775+
776+
test "transaction logs begin :commit and :rollback" do
777+
stack = [
778+
{:ok, :state},
779+
{:commit, :new_state},
780+
{:rollback, :newer_state},
781+
]
782+
{:ok, agent} = A.start_link(stack)
783+
784+
parent = self()
785+
opts = [agent: agent, parent: parent]
786+
{:ok, pool} = P.start_link(opts)
787+
788+
log = &send(parent, &1)
789+
790+
assert P.transaction(pool, fn(_) -> flunk("transaction ran") end,
791+
[log: log]) == {:error, :rollback}
792+
793+
assert_received %DBConnection.LogEntry{call: :begin} = entry
794+
err = DBConnection.TransactionError.exception(:commit)
795+
assert %{query: :begin, params: nil, result: {:error, ^err}} = entry
796+
assert is_integer(entry.pool_time)
797+
assert entry.pool_time >= 0
798+
assert is_integer(entry.connection_time)
799+
assert entry.connection_time >= 0
800+
assert is_nil(entry.decode_time)
801+
802+
refute_received %DBConnection.LogEntry{}
803+
804+
assert P.transaction(pool, fn(_) -> flunk("transaction ran") end,
805+
[log: log]) == {:error, :rollback}
806+
807+
assert_received %DBConnection.LogEntry{call: :begin} = entry
808+
err = DBConnection.TransactionError.exception(:rollback)
809+
assert %{query: :begin, params: nil, result: {:error, ^err}} = entry
810+
assert is_integer(entry.pool_time)
811+
assert entry.pool_time >= 0
812+
assert is_integer(entry.connection_time)
813+
assert entry.connection_time >= 0
814+
assert is_nil(entry.decode_time)
815+
816+
refute_received %DBConnection.LogEntry{}
817+
818+
assert [
819+
connect: [_],
820+
handle_begin: [ _, :state],
821+
handle_begin: [_, :new_state]] = A.record(agent)
822+
end
823+
824+
test "transaction logs commit :begin and :rollback" do
825+
stack = [
826+
{:ok, :state},
827+
{:ok, :began, :new_state},
828+
{:begin, :newer_state},
829+
{:ok, :began, :newest_state},
830+
{:rollback, :state2},
831+
{:ok, :rolledback, :new_state2}
832+
]
833+
{:ok, agent} = A.start_link(stack)
834+
835+
parent = self()
836+
opts = [agent: agent, parent: parent]
837+
{:ok, pool} = P.start_link(opts)
838+
839+
log = &send(parent, &1)
840+
841+
assert P.transaction(pool,
842+
fn(_) -> assert_receive %DBConnection.LogEntry{call: :begin} end,
843+
[log: log]) == {:error, :rollback}
844+
845+
assert_received %DBConnection.LogEntry{call: :commit} = entry
846+
err = DBConnection.TransactionError.exception(:begin)
847+
assert %{query: :commit, params: nil, result: {:error, ^err}} = entry
848+
assert is_nil(entry.pool_time)
849+
assert is_integer(entry.connection_time)
850+
assert entry.connection_time >= 0
851+
assert is_nil(entry.decode_time)
852+
853+
refute_received %DBConnection.LogEntry{}
854+
855+
assert P.transaction(pool,
856+
fn(_) -> assert_receive %DBConnection.LogEntry{call: :begin} end,
857+
[log: log]) == {:error, :rollback}
858+
859+
assert_received %DBConnection.LogEntry{call: :commit} = entry
860+
assert %{query: :rollback, params: nil, result: {:ok, :rolledback}} = entry
861+
assert is_nil(entry.pool_time)
862+
assert is_integer(entry.connection_time)
863+
assert entry.connection_time >= 0
864+
assert is_nil(entry.decode_time)
865+
866+
refute_received %DBConnection.LogEntry{}
867+
868+
assert [
869+
connect: [_],
870+
handle_begin: [ _, :state],
871+
handle_commit: [_, :new_state],
872+
handle_begin: [_, :newer_state],
873+
handle_commit: [_, :newest_state],
874+
handle_rollback: [_, :state2]] = A.record(agent)
875+
end
876+
877+
test "transaction logs rollback :begin" do
878+
stack = [
879+
{:ok, :state},
880+
{:ok, :began, :new_state},
881+
{:begin, :newer_state}
882+
]
883+
{:ok, agent} = A.start_link(stack)
884+
885+
parent = self()
886+
opts = [agent: agent, parent: parent]
887+
{:ok, pool} = P.start_link(opts)
888+
889+
log = &send(parent, &1)
890+
891+
assert P.transaction(pool,
892+
fn(conn) ->
893+
assert_receive %DBConnection.LogEntry{call: :begin}
894+
P.rollback(conn, :oops)
895+
end,
896+
[log: log]) == {:error, :oops}
897+
898+
assert_received %DBConnection.LogEntry{call: :rollback} = entry
899+
err = DBConnection.TransactionError.exception(:begin)
900+
assert %{query: :rollback, params: nil, result: {:error, ^err}} = entry
901+
assert is_nil(entry.pool_time)
902+
assert is_integer(entry.connection_time)
903+
assert entry.connection_time >= 0
904+
assert is_nil(entry.decode_time)
905+
906+
refute_received %DBConnection.LogEntry{}
907+
908+
assert [
909+
connect: [_],
910+
handle_begin: [ _, :state],
911+
handle_rollback: [_, :new_state]] = A.record(agent)
912+
end
913+
914+
test "transaction with explicit begin/rollback/commit call causes rollback" do
915+
stack = [
916+
{:ok, :state},
917+
{:ok, :began, :new_state},
918+
{:ok, :rolledback, :newer_state},
919+
{:ok, :began, :newest_state},
920+
{:ok, :rolledback, :state2},
921+
{:ok, :began, :new_state2},
922+
{:ok, :rolledback, :newer_state2}
923+
]
924+
{:ok, agent} = A.start_link(stack)
925+
926+
opts = [agent: agent, parent: self()]
927+
{:ok, pool} = P.start_link(opts)
928+
929+
assert P.transaction(pool, fn(conn) ->
930+
assert_raise DBConnection.ConnectionError,
931+
"can not begin inside legacy transaction",
932+
fn -> P.begin!(conn) end
933+
end) == {:error, :rollback}
934+
935+
assert P.transaction(pool, fn(conn) ->
936+
assert_raise DBConnection.ConnectionError,
937+
"can not rollback inside legacy transaction",
938+
fn -> P.rollback!(conn) end
939+
end) == {:error, :rollback}
940+
941+
assert P.transaction(pool, fn(conn) ->
942+
assert_raise DBConnection.ConnectionError,
943+
"can not commit inside legacy transaction",
944+
fn -> P.commit!(conn) end
945+
end) == {:error, :rollback}
946+
947+
assert [
948+
connect: [_],
949+
handle_begin: [ _, :state],
950+
handle_rollback: [_, :new_state],
951+
handle_begin: [ _, :newer_state],
952+
handle_rollback: [_, :newest_state],
953+
handle_begin: [_, :state2],
954+
handle_rollback: [_, :new_state2]] = A.record(agent)
955+
end
776956
end

0 commit comments

Comments
 (0)