[Dbix-class] Apache::DBI and DBIx::Class

Brandon Black blblack at gmail.com
Wed Apr 5 20:56:35 CEST 2006


On 4/5/06, Mark Hedges <hedges at ucsd.edu> wrote:
>
> On Wed, 5 Apr 2006, Brandon Black wrote:
> >
> > Yes.  Basically, what Apache::DBI does is enable a hook in DBI itself
> > to cache connections based on connect info.  If you call ->connect
> > twice with the same arguments, and the old connection is still up, it
> > will re-use that connection.  The idea (AFAIK) was to help converting
> > legacy CGI applications which might have been designed to ->connect to
> > the database on every request, into mod_perl applications that keep
> > their connection persistent.  In an application designed for mod_perl
> > (or other persistent) usage that knows to only connect once at process
> > startup, Apache::DBI doesn't help you.
>
> I've always found in a handler that you have to "connect" at
> the beginning of every request, because sometimes the child
> sits around until the database times out the connection.
> (Which is a good thing, unless you have unlimited DB memory.)
>
> Calling DBI->connect is the best way to get Apache::DBI to
> ping the database handle and reconnect if it went away.
> Apache::DBI by default does a $dbh->ping first on connect (you
> can change the timing behavior.)  Then it reconnects if the
> ping failed.
>
> So any good mod_perl application should make use of Apache::DBI
> and "connect" on every request.  If it's busy, a ping per request
> is a lot less overhead than a connect.  If it's not busy, and
> you don't use Apache::DBI and connect every request, the handler
> will eventually try operations on a stale handle, which is bad.
>

Or you can not use Apache::DBI, just connect once, and then do a "ping
and reconnect if neccesary" check yourself at the top of each request
instead of a "connect".  It's the same thing either way.  Apache::DBI
saves you a tiny bit of code, but the downside is that it overrides
the behavior of DBI->connect and makes it do things it wouldn't
otherwise do (like cache connections it shouldn't).  And then because
of the shared-interpreter style of mod_perl, it is changing this
behavior for all other applications silently, whether they wanted or
knew about Apache::DBI or not.  The combination of mod_perl and
Apache::DBI is an Evil thing in my book, but it's so widely used that
we have to play nice with it one way or another.

> If DBIC were o.k. with Apache::DBI, all that would be needed
> would be a class method to cause DBIC to do a DBI->connect.
> This method should be called at the start of a mod_perl handler
> request.  Then let Apache::DBI ping the connection, and it will
> go connect it again if it's stale.
>

Right now, DBIC actually does a ->ping, ->{Active} check, and a
thread/process-id check before every statement execution, and
reconnects as neccesary when the checks fail.  This is less efficient
than we'd like, but it produces correct results in any environment,
whether or not we're dealing with mod_perl and/or Apache::DBI.  DBIC
code has to run right in other multi-process/thread environments
outside of Apache as well, so our solutions have to be general.

We've been talking for a while about transitioning to an
exception-based model instead, which will look the same to the user. 
The basic idea is to blindly use the existing $dbh and trap
exceptions.  Check ->ping && ->{Active} (and for thread changes) when
an exception happens, and reconnect and retry the
statement/transaction if the exception was due to a disconnected $dbh.
 The only thing that would still need to be checked explicitly before
each statement would be the process-id (as forking doesn't neccesarily
cause an immediate failure, it just leads to race conditions down the
road - whereas threadid changes and server disconnects definitely do
cause immediate $dbh statement failure).  This work hasn't actually
happened yet.

> It makes sense that if DBIC isolates itself from Apache::DBI,
> that if any part of the process connects to the database in some
> other way (which my AuthenHandler does to be quick about it),
> that would result in twice the number of connections needed.
>
> But since Apache::DBI is useful, why not let DBIC ignore it and
> let it do its job?  The responsibility would be on the mod_perl
> coder to know that the connection handle would be shared, and
> transactions as such would be touchy.  (But if you need that
> kind of speed, you're probably using MyISAM anyway.)

Apache::DBI is not useful to DBIC itself.  From perspective of a DBIC
itself, or an isolated application which only uses DBIC for database
access, regardless of whether we're under mod_perl or not, Apache::DBI
is a problem rather than a problem-solver.  DBIC already manages
connections in a more advanced way than Apache::DBI is capable of.

The exception that's been brought up in this thread is when you have a
legacy non-DBIC Apache::DBI-using app (or your Authen Handler you
mentioned) and a DBIC app sharing the same mod_perl and the same
database, they don't share connections as one would like them to.

However, if DBIC just "ignored" Apache::DBI's presence and let the
connections share via that mechanism, it would break DBIC's ability to
cleanly handle forks and thread-spawning correctly (which Apache::DBI
doesn't do at all).  Basically, Apache::DBI's "silently override the
->connect method" crap interferes with attempts at more-advanced
database handle management.

There probably is a way to make it work and share connections with
Apache::DBI where possible, I just haven't sorted it out yet.  But it
will require detecting the prescence of Apache::DBI and taking some
pains to work around/with it, as opposed to just ignoring the
existance of it, and it's probably something that shouldn't be enabled
by default (although that's up to the collective opinion around here
obviously).

-- Brandon



More information about the Dbix-class mailing list