DBM::Deep: Problems with transactions

474 views Asked by At

I've never done transactions (in terms of programming), therefore I don't know if there is something wrong with my script or something else:

#!/usr/bin/env perl
use warnings;
use 5.012;
use DBM::Deep;

my $db = DBM::Deep->new( 'foo.db' );

my $trans = $db->supports( 'transactions' );
say 'Does ', $trans ? '' : 'NOT ', 'support transactions'; 

$db->{key} = 'value';
$db->begin_work;
$db->{key1} = 'value2';
$db->rollback;
$db->{key1} = 'value1';
$db->commit;

Output:

# Does support transactions
# DBM::Deep: Cannot allocate transaction ID at ./perl1.pl line 12

Part of comment:

my $db = DBM::Deep->new( file => 'my.db', num_txns => 1 );

$db->{key} = 'value';
$db->begin_work;
$db->{key1} = 'value2';
$db->rollback;
$db->begin_work;
$db->{key1} = 'value1';
$db->commit;
2

There are 2 answers

1
Rob Kinyon On BEST ANSWER

Sorry about taking so long to answer this question - I only just found it a few days ago. (I'm the maintainer of DBM::Deep.)

The issue is that num_txns is only set when the file is created. (This is because of how the DBM file is laid out on disk.) Once you've created a DBM file, then the num_txns value is read from the file and ignored in the call to new(). So, once you changed your invocation to specify num_txns, it wouldn't help unless you also used a new DBM file.

While I cannot change this behavior without significantly changing how the DBM file structure works (which may be a good idea, but is a huge thing to do), you should have been warned and there should have been better documentation. I have opened https://github.com/robkinyon/dbm-deep/issues/12 to track this problem and the fix(es) for it.

5
Joel Berger On

Accoring to the documentation the rollback command ends the transaction.

rollback() This discards the changes done within the transaction to the mainline and ends the transaction.

Therefore you need to start a new transaction after a rollback.

$db->{key} = 'value';
$db->begin_work;
$db->{key1} = 'value2';
$db->rollback;
$db->begin_work;
$db->{key1} = 'value1';
$db->commit;

or you could do something like

sub my_rollback {
  my $db = shift;
  $db->rollback();
  $db->begin_work();
}

$db->{key} = 'value';
$db->begin_work;
$db->{key1} = 'value2';
my_rollback $db;
$db->{key1} = 'value1';
$db->commit;

or with a little black magic, you can keep the OO style

sub my_rollback {
  my $db = shift;
  $db->rollback();
  $db->begin_work();
};
{
  no strict 'refs';
  *{'DBM::Deep::my_rollback'} = \&my_rollback;
}

$db->{key} = 'value';
$db->begin_work;
$db->{key1} = 'value2';
$db->my_rollback;
$db->{key1} = 'value1';
$db->commit;