The problem:

ithreads-enabled Perl cannot share nested (Perl/C) datastructures. If it could
the issue of dbi-pool would be a no-brainer since all we needed to do
is $dbh ||= connect();

An attempted solution:

if Perl doesn't share nested datastructures, we will implement this
sharing ourselves, using an items pool. After struggling with DBI guts
and implementing this solution the result wasn't good: perl context
can't be easily switched, so this doesn't work. I suppose that we
could always create $dbh in the parent perl, and switch the context to
it every time we needed to work with it, but that would result in
serialization of all threads wanting to work with any $dbh, resulting
in really bad performance, defeating the purpose.

Final (semi-working) solution:

as discussed with Tim and Hugo back at TPC (Jul 2002), the only
possible solution is to share only the driver's private part of
dbh. This is only possible if that private part has nothing to do with
Perl, i.e. the implementation is in C and the threads implementation
shares all the data between all threads (dunno if it's through for all
threads libs, most likely is).

I didn't want to mess directly with DBI, so I've created DBI::Pool,
which is a hack which should eventually fold into DBI itself.

Currently DBI::Pool is actually 3 things:

1. XS implementation of a basic free/busy lists management
   this is used to store and retrieve the data structures. I believe
   that this can be moved into Thread::Pool so it'll support sharing
   of C data-structures as well. But for now, it's here.

   it includes several functions for a more general Thread::Pool (like
   grow()), which is unused in DBI::Pool.

2. XS functions imp_dbh2dbh() and dbh2imp_dbh() which extract and
   restore the private dbh data from/to $dbh

3. overriden DBI's connect/disconnect to work with the pool (pure
   Perl mess)

As mentioned before, this is just a proof of concept which has lots of
loose ends. Though I can't continue any further, without getting some
help from DBI gurus. The open issues are listed next.

Please notice that I've chosen to work with DBD::mysql (because that's
the only db that I have here) and if you want to try my hack at work,
you need to have it as well. Also you need to have a database 'test'
and be able to connect with empty l/p, or change test.pl to adjust to
your setup.

Also you need the ithreads-enabled perl-5.8.0 or higher. It'll build
with ithreads-disabled perl as well, but I haven't adjusted the Perl
code to work without the threads.

These are the open issues:

1. Because I needed to access the private data of the dbd driver, I
   needed to include the mysql header and struct imp_dbh_st. So please
   adjust the path to mysql.h if it's in a different location.

once the logic folds into DBI, this won't be necessary

2. I need a support from DBI to help me access the *really* private
   data in struct imp_dbh_st, because the following is a hack:

    D_imp_dbh(dbh);
    imp_dbh->mysql = ((imp_dbh_t *)imp_dbh_new)->mysql;

   When I re-install the stored dbh, I must not break the ->com
   structure, but overwrite the rest. So I guess the right approach is
   to copy away the original ->com, overwrite the whole imp_dbh and
   then copy back the original ->com. Also I'd prefer to store in the
   pool only the really private data. I guess all I need is to know
   the size of ->com struct with its sub-structs, preferrably at
   compile time.

3. $dbh->DESTROY. Currently I had to:

SvREFCNT_inc(dbh);

   so imp_dbh won't lose it's data when $dbh goes out of scope, I have
   tried copying it but wasn't very successful. Neither playing with
   DBIc_FLAGS(imp_dbh) helped, but that's probably because I'm not
   very familiar with DBI guts. You help is needed here.

4. If you run 'make test' there could be segfaults when perl_destruct
   is called because DBI is getting confused about what to destroy
   (probably due to #3 above)

5. Finally, the most important issue is that if a thread logged in for
   real and created imp_dbh, it must not exit while other threads use
   the same data. The solution to this is either have to switch
   context to the parent's perl when doing the real login(), or we can
   try to memcpy() the private datastructure while being in the
   parent's context (e.g. by creating an SV), but this all goes back
   to #3 above. this could be a bottleneck, but I'm not sure.

   (as a result if you arrange for the first thread to quit early in
   test.pl, it'll segfault)

6. Notice that the parent thread creates $obj, which serves as a
   warehouse for all items (imp_dbh). So as long as DBI::Pool is
   loaded in the parent thread, this is not an issue. we could easily
   test for that:

   die "must be loaded in the parent thread"
        unless threads->self->tid() == 0;

7. I think this approach should work for non-thread perl all the same,
   just need to do some minor tweaks, like installing a NOOP for
   share() and lock(), or move these into XS protected by
   #ifdef USE_ITHREADS / #endif

8. and the overriding of connect/disconnect could be done in a better
   way (I think the current implementation might have problems), but
   it's good enough for getting started.

9. The current implementation disregards the login/passw/attr and
   assumes that there is only one pool. Let's tackle each problem at a
   time. Once previous 8 issues are resolved we can move onto this
   one.

The DBI::Pool package is attached. To run it, do:

perl Makefile.PL
make
make test

if 'make' fails, adjust the hardcoded mysql bits in Pool.xs.

'make test' may segfault, that's ok.

__________________________________________________________________
Stas Bekman            JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/     mod_perl Guide ---> http://perl.apache.org
mailto:[EMAIL PROTECTED] http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org   http://ticketmaster.com

Attachment: DBI-Pool-0.01.tar.gz
Description: application/gzip



Reply via email to