I've been writing some Perl DBI code which involves some fairly involved error handling; I've been looking for a way to roll back transactions neatly when certain errors happen.
I very nearly reinvented the concept of a 'transaction scope guard' which I now find is implemented in DBIx::Class (with Scope::Guard implementing a more general version). A lexical variable can be used to detect in which cases a transaction should be ended, because the object it points to will get DESTROYed when it goes out of scope. Some rough code to illustrate the concept is below.
# $fallback controls whether we use nested transactions, # which is slower but lets us commit all the other lines in the batch. sub process_batch { my $fallback = shift; # This catches any exception handling that makes us leave the function. # The DESTROY method of $transaction contains $db->rollback() my $transaction = $db->txn_scope_guard(); for (1..2000) { # File access error will get thrown to outside of function, # so the transaction will be rolled back. my $line = get_next_line(); last unless $line; my $parsed; eval {$parsed = parse_line($line)}; if ($@) { # Handle line parsing errors here without interfering with # DB error handling. warn "Could not parse $line\n"; next; } # here's the actual db code eval { $db->savepoint() if $fallback; $db->process_one_line($parsed); } if ($@) { if ($fallback) { # Just roll back to savepoint. Transaction continues. $db->rollback_to_savepoint(); } else { # Propagate error outside of function, ending transaction. die $@; } } } $transaction->commit(); }
The advantage is that rollback code can be kept in one place, and not repeated all over the various error handling cases.
I had actually gone off this idea, because I couldn't see any documentation defining exactly when the DESTROY method gets called in Perl. But given it's got into DBIx::Class, it must be fine! I also prefer their API over what I was considering implementing; the transaction object that gets returned will handle only commit(), not any other database calls.