Sharing a DBI handle between Class::DBI and Rose::DB::Object

samtregar on 2008-04-03T20:12:31

At my current job we've got a large existing code-base built on Class::DBI. For my current project I decided to experiment with using Rose::DB::Object instead, hoping to see less need for hand-written SQL. So far it's been a success, but one issue was quite difficult to get right - getting the two systems to share a DBI handle. If I didn't do this then I was going to see a doubling of total DBI connections when I deployed my new app to the web cluster, which is unacceptable.

I started with the most obvious solution, an over-ridden init_db() in my Rose::DB::Object subclass:

sub init_db {
    my ($pkg, @args) = @_;

    My::Rose::DB->new_or_cached(dbh => My::Class::DBI->db_Main(), @args);
}

That worked great at first - when Rose needs a DB connection it gets one pre-loaded with my Class::DBI handle. (And as a side-note, the fact that the Class::DBI handle uses DBIx::ContextualFetch doesn't cause Rose problems.)

However, sometimes for unknown reasons Rose will decide it needs to reconnect after the initial connection is established. To intercept these calls I added an overridden dbh() method to my Rose::DB sub-class:

sub dbh {
    my $self = shift;
    unless (@_) {
        $self->{dbh} ||= My::Class::DBI->db_Main();
    }
    return $self->SUPER::dbh(@_);
}

Another useful hint which helped me notice very quickly when Rose decided to reconnect - I gave Rose::DB an invalid password in my call to register_db. That way Rose would have all the correct information about the connection, but wouldn't be able to initiate new connections.

I hope this helps other suffering Class::DBI users to give Rose a try!

-sam