On Wed, Mar 31, 2004 at 10:20:16PM +0100, Tim Bunce wrote:
> sub perform_transaction {
...
> }
>
> But as I tinker with it I wonder if it's a solution chasing a problem.
> Where, and how, and why, would it be used?
>
> Anyone got any realistic examples?
Well, yes, but it's 350 lines long and uses a couple of other modules
and subroutines. This is part of an in-house project that unfortunately
isn't open source, and right now the routine is rather tied into the
project.
I've written an object layer on top of DBI, sortof like DBIx, but
$conn->Transaction() was always part of that, for a couple of reasons.
Features include:
The code in Transaction will auto-restart a configurable number of times
(and with configurable exponential-backoff delays) in case a restartable
database error occurs (eg deadlock, serialisation error). This proved
_very_ useful. It is highly dependent on the database used, though, as
you need to match against the text of error messages (maybe a
$dbh->{restartable_db_errors_regex} is useful, to be filled in by
DBD authors?)
Nested Transactions are supported/emulated as a single transaction. This
is very useful, as utility functions can simply use their own
Transaction() block, without having to worry when they get integrated in
some bigger function, which combines more actions in the same
transaction... this just transparently works.
Consistent error trapping and reporting. In our case we build an array
of matches against error objects and coderefs that include the default
stuff like "delay and restart on serialisation errors", "rollback
silently on rollback requests", which can be extended by the calling
routine. Customised error traps still work on nested transactions.
"unexpected" die()s are mailed to the developers.
The DBIx-alike objects are automatically tracked to make sure you do
not accidentally modify an object that's initialised out of your
transaction, otherwise your transaction wouldn't be restartable.
(This part wouldn't be in DBI, and frankly, I should take this check
out because it complicates things considerably)
Can abort transactions that are running too long by using an alarm.
(Recent addition after some idiot programmed user interaction in
the middle of a transaction. Tar and feathers can (and should) also be
used to solve this).
Automatically rollbacks uncommitted transactions (you need to explicitly
commit, as commits may raise error conditions too, but that's a
taste issue).
Most of the above has been pretty useful in the past 4 years in
which this code was used. I wouldn't mind posting the code to allow
others to take ideas from it, but I fear it won't be actual
"working" code without a lot of the support routines around it.
Hmm... and the documentation for the function is in Dutch (but
code comments and variables are mostly in English).
Oh, wrt calling syntax: our Transaction subroutine takes exactly the
same arguments as your proposed "Mark II" Transaction routine :)
($self, $attr, $code, @args). $attr is a hashref that's usually empty,
but can contain flags. We hardly ever use @args in practice, and the
$code is practically always passed as an inplace closure.
HTH,
--
#!perl -pl # mmfppfmpmmpp mmpffm <[EMAIL PROTECTED]>
$p=3-2*/[^\W\dmpf_]/i;s.[a-z]{$p}.vec($f=join('',$p-1?chr(sub{$_[0]*9+$_[1]*3+
$_[2]}->(map{/p|f/i+/f/i}split//,$&)+97):('m',p,f)[map{((ord$&)%32-1)/$_%3}(9,
3,1)]),5,1)='`'lt$&;$f.eig; # Jan-Pieter Cornet