On 8/15/07, Michael Lackhoff <[EMAIL PROTECTED]> wrote: > my $db = My::DB->new(); > > $db->do_transaction( > sub { > my $o = My::Thing->new(); > $o->foo(123); > $o->bar(456); > $o->save; > die "Does it rollback?"; > } > ); > > This code does _not_ do a rollback.
When running under Apache::DBI, this line issues a rollback: warn $o->db->dbh(); Here's the sequence: 1. $o->db->dbh() causes Rose::DB's init_dbh() to be called 2. init_dbh() calls DBI->connect() 3. Under Apache::DBI, DBI->connect() calls Apache::DBI::connect() 4. When Apache::DBI sees that there's already an existing $dbh that satisfies the connect request, it calls Apache::DBI::reset_startup_state() 5. Apache::DBI::reset_startup_state() contains this: # Rollback current transaction if currently in one $Connected{$Idx}->{Active} and !$Connected{$Idx}->{AutoCommit} and eval {$Connected{$Idx}->rollback}; foreach my $key (@attrs) { $Connected{$Idx}->{$key} = $Connected{$Idx}->{private_Apache_DBI}{$key}; } And there you have it. The $dbh that belongs to the $db that you called do_transaction() on has now been rolled back, and it's had its AutoCommit attribute set back to what it was on connect (1 in your case). So, the question is, what do we do to fix this? Obviously, one work around is to pass the same $db around to all objects. You can also change your init_db() routine in your common base class to be this instead: our $DB; sub init_db { $DB ||= My::DB->new } But really, I'd like to solve this somehow so the default way works as well. It would be nice if I could ask Apache::DBI not to call reset_startup_state(), perhaps by passing a special connect attribute: DBI->connect(..., { private_Apache_DBI_No_Reset => 1, ... }); Or maybe just an attribute to tell it to preserve the current transaction: DBI->connect(..., { private_Apache_DBI_Preserve_Transaction => 1, ... }); In the absence of such attributes, I think Apache::DBI should, at the very least, warn() if an existing transaction is rolled back or if AutoCommit is altered. (The exception is the cleanup handler, which should cleanup silently.) Another alternative is an Apache::DBI method that I can ask for an existing $dbh that will satisfy a connect() request. So rather than this: $dbh = DBI->connect(@args); I'd do this in init_dbh() instead when running under Apache::DBI: my $dbh = Apache::DBI->cached_dbh(@args) || DBI->connect(@args); where Apache::DBI->cached_dbh() would simply return me the any existing, cached $dbh without modifying it in any way (except perhaps to do a ping check). Finally, I suppose I could change the default implementation of init_db() to be more like this instead: sub init_db { $DB ||= Rose::DB->new } But that'd be quite a behavior change, and it'd have its own set of problems. So, what do you all think? Suggestions? -John ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ Rose-db-object mailing list Rose-db-object@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rose-db-object