One of my client projects has suffered from running code too quickly (go Perl!) and too much in parallel (go Unix processes!) and has kept me tuning the transactional model of the backend storage.

I'm not worried about inconsistencies, but rather detecting and avoiding lock contention where possible, rescheduling transactions where lock contention does occur, and above all, wrapping transactions in the smallest units possible.

The fantastic (but not for all projects) KiokuDB has been very useful for this project. If you use it with a transactional backend, it provides a DBI-inspired method to invoke a function reference in its own transaction:

$dir->txn_do( sub { $dir->delete( @args ) } );

I was glad of the ability to pass around closures in a lightweight manner; delaying computation until KiokuDB has set up the transaction is very useful. Even so, I was less pleased with all of the syntactic noise littering my code in several places.

That's because I wasn't taking full advantage of the abstraction possibilities of late binding.

Now I have instead:

use Try::Tiny; sub do_txn { my ($self, $method, @args) = @_; my $dir = $self->dir; my $sub = sub { $dir->$method( @args ) }; while ( ... ) { try { $dir->txn_do( $sub ) } catch { ... } } ... }

... and I can call it with:

$self->do_txn( add => $new_obj ); $self->do_txn( delete => @args ); $self->do_txn( update => $invocant );

... to remove visual clutter from other parts of the code. Better yet, all of the retrying semantics are in one place, and I can add logging or tuning there.

I hoisted the creation of the closure passed to txn_do() out of the while loop for two reasons. Primarily, I believe doing so makes the code within the loop clearer. It's also slightly more efficient (slightly) to create the closure once than on every trip through the loop. (If efficiency were of greater concern—and lock contention is much more troublesome here—I could pre-resolve the method and bind to the function reference representing the candidate method first instead of the name of the method, but that would add at least one line of code and possibly a few more for error checking, and it's not worthwhile yet.)