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

Reply via email to