Close a transaction when returning from within its block (#167)

This commit is contained in:
Jamie Gaskins 2022-10-27 12:35:57 -04:00 committed by GitHub
parent 167b55966e
commit e076a08cd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 10 deletions

View file

@ -95,6 +95,20 @@ describe DB::Transaction do
t.committed.should be_false t.committed.should be_false
end end
it "transaction with block from connection should be committed if `return` is called" do
t = uninitialized DummyDriver::DummyTransaction
with_witness do |w|
with_dummy_connection do |cnn|
t = return_from_txn(cnn).as(DummyDriver::DummyTransaction)
w.check
end
end
t.rolledback.should be_false
t.committed.should be_true
end
it "transaction can be committed within block" do it "transaction can be committed within block" do
with_dummy_connection do |cnn| with_dummy_connection do |cnn|
cnn.transaction do |tx| cnn.transaction do |tx|
@ -211,3 +225,9 @@ describe DB::Transaction do
end end
end end
end end
private def return_from_txn(cnn)
cnn.transaction do |tx|
return tx
end
end

View file

@ -13,25 +13,26 @@ module DB
# can be called explicitly. # can be called explicitly.
# Returns the value of the block. # Returns the value of the block.
def transaction(& : Transaction -> T) : T? forall T def transaction(& : Transaction -> T) : T? forall T
rollback = false
# TODO: Cast to workaround crystal-lang/crystal#9483 # TODO: Cast to workaround crystal-lang/crystal#9483
# begin_transaction returns a Tx where Tx < Transaction # begin_transaction returns a Tx where Tx < Transaction
tx = begin_transaction.as(Transaction) tx = begin_transaction.as(Transaction)
begin begin
res = yield tx res = yield tx
rescue DB::Rollback rescue DB::Rollback
tx.rollback unless tx.closed? rollback = true
res res
rescue e rescue e
unless tx.closed? rollback = true
# Ignore error in rollback.
# It would only be a secondary error to the original one, caused by
# corrupted connection state.
tx.rollback rescue nil
end
raise e raise e
else ensure
tx.commit unless tx.closed? unless tx.closed?
res if rollback
tx.rollback
else
tx.commit
end
end
end end
end end
end end