You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Michael Ludwig <ml...@as-guides.com> on 2009/01/14 16:12:34 UTC

Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

I want to build a mod_perl2 application using Sleepycat::DbXml. This is
the Perl bindings to the C++ interface to Berkeley DB and Berkeley DB
XML, developed by Sleepycat, now owned by Oracle (keywords: DbEnv,
XmlManager, XmlContainer).

Has anyone ever used this combination?

Note I'm using Apache 2.2 with the worker MPM and a suitably configured
Perl (perl -V:useithreads -V:usemultiplicity).

I want to open the environment and the XML container (the database)
during initialization so this doesn't have to be done anew on each
request.

However, I don't know how to do this. Currently, I'm trying to set up
things in startup.pl (loaded via PerlPostConfigRequire), store the
database environment handle in a global package variable, and access
that from the PerlContentHandler.

(1) Is that okay in principle?

(2) As I'm getting unfriendly SEGVs: Has anyone any experience on using
mod_perl2 with Sleepycat::Db or Sleepycat::DbXml? If so, I can provide
more details on this.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
I've referenced this thread on the Oracle Berkeley DB forum.
Chances are it'll catch the attention of one of the BDB experts.
If you're interested, please see:

http://forums.oracle.com/forums/thread.jspa?threadID=848390

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Perrin Harkins schrieb:
> On Tue, Jan 20, 2009 at 4:19 PM, Michael Ludwig <ml...@as-guides.com>
> wrote:
>> On the other hand, performance isn't what you'd call great, as access
>> to the database is effectively serialized.
>
> Are you sure about that?

I'm sure about the queries seeming *effectively* serialized, judging
from the timing using the ab tool to test with different concurrency
levels.

> Berkeley DB doesn't work that way.  It has read and write locks which
> can be set to work at a (memory) page level rather than a database
> level.

I know. I don't think this is a limitation of Berkeley DB or DB XML at
all. I think it may stem from me being unable to set up the environment
properly in the mod_perl context.

The query in question uses an index and runs in a transaction flagged
DB_READ_COMMITTED. There aren't any concurrent writes, though.

>> I think a single-process, multi-threaded approach (as in Java) will
>> work best for Berkeley DB.
>
> I don't know about dbXML, but Berkeley DB is designed for
> multi-process use.  It uses shared memory and locks to coordinate.

Berkeley DB XML is built on top of Berkeley DB, so it is the same in
this regard, as can be seen on slides 12 through 17 (especially 14) of
this nice, new video tutorial:

http://www.oracle.com/technology/products/berkeley-db/tutorial-berkeleydb-ds.html

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Perrin Harkins <ph...@gmail.com>.
On Tue, Jan 20, 2009 at 4:19 PM, Michael Ludwig <ml...@as-guides.com> wrote:
> On the other hand, performance isn't what you'd call great, as access to
> the database is effectively serialized.

Are you sure about that?  Berkeley DB doesn't work that way.  It has
read and write locks which can be set to work at a (memory) page level
rather than a database level.

> I think a single-process, multi-threaded approach (as in Java) will work
> best for Berkeley DB.

I don't know about dbXML, but Berkeley DB is designed for
multi-process use.  It uses shared memory and locks to coordinate.

- Perrin

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
A sidenote:

I've built more or less the same Apache as a prefork MPM, and I'm not
seeing any SEGVs any more.

On the other hand, performance isn't what you'd call great, as access to
the database is effectively serialized. Thirty queries will take thirty
seconds, regardless of concurrency level. This may have been a bit
faster in the worker MPM. (But only due to the SEGVs, unfortunately,
which came across pretty good, performance-wise.)

I think a single-process, multi-threaded approach (as in Java) will work
best for Berkeley DB.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Peters <mp...@plusthree.com>.
craig@animalhead.com wrote:

> I have a vague recollection of reading about the
> circumstances in which filehandles can be inherited, but
> can't remember where. 

I've been bitten by this a few times. Filehandles (and thus sockets) are inherited across forks. If 
your system isn't very busy you won't notice a problem. But when things heat up you will start to 
get very strange problems. And I do mean strange.

I was doing something with Inline::Java (which communicates over a socket to a JVM) under mod_perl 
and the communication was getting all garbled. Multiple processes were sending and reading off the 
socket which confused the other processes since they were all getting partial reads on their data 
and partial reads from the data for other processes. But the problems only happened during the 
busiest 2 weeks of the year and were fine the rest of the time. But those weren't very fun weeks :)

-- 
Michael Peters
Plus Three, LP


Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by cr...@animalhead.com.
This is my first time replying to the list.  I've seen
advice about not being able to share a filehandle opened
in a pre-fork stage before, but have two counter-examples:

1) I opened a log file for write/append in the open-logs
stage of a module, and was able to (flock and) write to it
in child processes.  (It's called the open-logs stage!)

2) My web site ties its read-only DBs at the post-config
stage and reads them in the child httpd's.

My one RW DB (for guestbook filtering) has an accompanying
lockfile that's opened and flocked before the DB is tied,
on the fly.

I have a vague recollection of reading about the
circumstances in which filehandles can be inherited, but
can't remember where.  Programming Perl?

Sorry to make things more complex, but I'll be happy if
someone can clarify this.

cmac
www.animalhead.com

On Jan 14, 2009, at 8:18 AM, Mark Hedges wrote:
> On Wed, 14 Jan 2009, Michael Ludwig wrote:
>
>> I want to build a mod_perl2 application using
>> Sleepycat::DbXml. This is
>>
>> However, I don't know how to do this. Currently, I'm
>> trying to set up things in startup.pl (loaded via
>> PerlPostConfigRequire), store the database environment
>> handle in a global package variable, and access that from
>> the PerlContentHandler.
>
> This probably won't work since the filehandle cannot be
> shared among mod_perl children.
>
> Your startup.pl script is running before the root apache
> process forks into the child processes.  Scalars, lists and
> hashes will be cloned, but file handles won't, they have to
> be opened.
>
> Probably what you're thinking of is a PerlChildInitHandler
> so that each mod_perl child process does your connection for
> you when the child process first forks.
>

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Torsten Foertsch schrieb:
> Of course it depends on your setup. If you configure only a small
> number of interpreters then the overall memory footprint can be lower
> then with prefork where each process runs its own interpreter. But the
> effect of the copied interpreters outweighs this very soon.

I see. Guess I have some reading to do to better understand what
"interpreters" as in Perl ithreads really means.

> Measuring memory consumption on Linux is not simple. You can't simply
> add up all RSS or VSZ figures or something like this. I know of 2
> approaches that work more or less well.
>
> 1) call mlockall in the parent and then use /proc/PID/smaps (or
> Linux::Smaps) to measure shared memory
>
> 2) take a system with enough RAM and nothing else to do, start apache
> with different numbers of children and watch the free memory with
> vmstat while doing so. Then you can estimate the amount of memory that
> is consumed by one apache process. See the attached picture for
> example.

Thanks for sharing this.

> You can run recovery in a separate process spawned at startup by the
> parent.

That's a good idea. And a simple one at that. Thank you!

Michael

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Torsten Foertsch <to...@gmx.net>.
On Mon 19 Jan 2009, Michael Ludwig wrote:
> > In fact, it is worse than fork()ing off unix processes because the
> > interpreter data is completely copied while a new interpreter is
> > created. On the other hand a forked process copies only those pages
> > that are written to.
>
> Thanks. If I'm understanding correctly and this is true (and not
> considering SEGVs), the total memory cost for a mod_perl on a worker
> MPM should outweigh that for mod_perl on a prefork MPM, which seems
> to not be aligned with what Craig has observed.

Of course it depends on your setup. If you configure only a small number 
of interpreters then the overall memory footprint can be lower then 
with prefork where each process runs its own interpreter. But the 
effect of the copied interpreters outweighs this very soon.

Measuring memory consumption on Linux is not simple. You can't simply 
add up all RSS or VSZ figures or something like this. I know of 2 
approaches that work more or less well.

1) call mlockall in the parent and then use /proc/PID/smaps (or 
Linux::Smaps) to measure shared memory

2) take a system with enough RAM and nothing else to do, start apache 
with different numbers of children and watch the free memory with 
vmstat while doing so. Then you can estimate the amount of memory that 
is consumed by one apache process. See the attached picture for 
example.

> In order to be on the safe side as far as Berkeley DB is concerned,
> you'd have to make sure up front that the environment is safe to use,
> which is done by running recovery (DB_RECOVER flag, DB_RUNRECOVERY
> error code) in a single thread or process that has exclusive access
> to the environment.

You can run recovery in a separate process spawned at startup by the 
parent.

Torsten

-- 
Need professional mod_perl support?
Just hire me: torsten.foertsch@gmx.net

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Torsten Foertsch schrieb:
> On Sun 18 Jan 2009, Michael Ludwig wrote:
>>
>> Hmm. Not sure what to make of this threaded Perl.
>
> In fact, it is worse than fork()ing off unix processes because the
> interpreter data is completely copied while a new interpreter is
> created. On the other hand a forked process copies only those pages
> that are written to.

Thanks. If I'm understanding correctly and this is true (and not
considering SEGVs), the total memory cost for a mod_perl on a worker
MPM should outweigh that for mod_perl on a prefork MPM, which seems
to not be aligned with what Craig has observed.

> I haven't used Sleepycat::DbXml but I use BerkeleyDB in production
> with prefork, see Apache2::Translation on CPAN.

Looks interesting. I'm going to take a look.

> What I have learned is, you can't open the BDB env in the parent
> apache. When I wrote that piece I also had problems when I opened the
> env in the parent and then closed it before forking children. Perhaps
> they are gone in a newer version.
>
> I think with prefork opening the env in a ChildInit handler should
> work. I ended up with the connect-at-first-usage approach
>
>   unless( $connected ) {
>     connect();
>     $connected=1;
>   }
>
> because I test almost all my modules also with a threaded MPM and the
> ChildInit hook is run once per process not once per Perl interpreter.

In order to be on the safe side as far as Berkeley DB is concerned,
you'd have to make sure up front that the environment is safe to use,
which is done by running recovery (DB_RECOVER flag, DB_RUNRECOVERY error
code) in a single thread or process that has exclusive access to the
environment.

Given that we're experiencing problems (SEGVs) when trying to open
the environment up front in the parent process, it looks like this
requirement is not easy to meet on the Apache/mod_perl platform.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Torsten Foertsch <to...@gmx.net>.
On Sun 18 Jan 2009, Michael Ludwig wrote:
>  From perldoc perlthrtut: "In this model each thread runs in its own
> Perl interpreter, and any data sharing between threads must be
> explicit." This does not sound to me as if there is a significant
> advantage over spawning child processes, at least not on UNIX.
>
> Hmm. Not sure what to make of this threaded Perl.

In fact, it is worse than fork()ing off unix processes because the 
interpreter data is completely copied while a new interpreter is 
created. On the other hand a forked process copies only those pages 
that are written to.

I haven't used Sleepycat::DbXml but I use BerkeleyDB in production with 
prefork, see Apache2::Translation on CPAN. What I have learned is, you 
can't open the BDB env in the parent apache. When I wrote that piece I 
also had problems when I opened the env in the parent and then closed 
it before forking children. Perhaps they are gone in a newer version.

I think with prefork opening the env in a ChildInit handler should work. 
I ended up with the connect-at-first-usage approach

  unless( $connected ) {
    connect();
    $connected=1;
  }

because I test almost all my modules also with a threaded MPM and the 
ChildInit hook is run once per process not once per Perl interpreter.

Torsten

-- 
Need professional mod_perl support?
Just hire me: torsten.foertsch@gmx.net

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Mark Hedges schrieb:
>
> Just curious, did you try eliminating every other module
> that you might be loading (anywhere in any module loaded by
> Apache)?   i.e. did you try a simple setup that only tries
> to open your database and does absolutely nothing else and
> loads no other CPAN modules.  With "use strict" and "use
> warnings FATAL => 'all'" of course.  --mark--

Time::HiRes was loaded in addition to the Apache2:: modules.
FATAL warnings weren't on, just plain warnings.

I changed the setup as follows:

     use strict;
     use warnings FATAL => 'all';

     use Apache2::RequestRec ();
     use Apache2::RequestIO ();
     use Apache2::Const -compile => qw( OK );
     use Apache2::RequestUtil ();
     use Apache2::Util ();

     use Apache2::Request ();

     use Sleepycat::Db;
     use Sleepycat::DbXml;

I dropped the query I was doing, which took about 0.7 seconds. So now
it's just opening the environment. Still seeing SEGVs.

I think the failure is related to how threads or processes are torn
down. What I'm getting is the following:

Database handles still open at environment close
Open database handle: tv.dbxml/structural_stats
Open database handle: tv.dbxml/secondary_document_statistics_decimal
Open database handle: tv.dbxml/secondary_document_index_decimal
Open database handle: tv.dbxml/secondary_document_statistics_date
Open database handle: tv.dbxml/secondary_document_index_date
Open database handle: tv.dbxml/secondary_document_statistics_string
Open database handle: tv.dbxml/secondary_document_index_string
Open database handle: tv.dbxml/secondary_document
Open database handle: tv.dbxml/content_document
Open database handle: tv.dbxml/secondary_dictionary
Open database handle: tv.dbxml/primary_dictionary
Open database handle: tv.dbxml/secondary_sequence
Open database handle: tv.dbxml/secondary_configuration
[Tue Jan 20 17:47:39 2009] [notice]
child pid 17142 exit signal Segmentation fault (11)

What does this mean? There is a file called tv.dbxml, which is the
Berkeley XML database (XmlContainer). The structural_stats etc are
named databases within that database files, as *file and *database
in open:

int
Db::open(DbTxn *txnid, const char *file,
     const char *database, DBTYPE type, u_int32_t flags, int mode);

http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html

I'm just realizing Google doesn't know about these database names yet.

The error message is pretty explicit about what's wrong here: "Database
handles still open at environment close". Indeed, I do not explicitly
close the handles. In a batch script, this doesn't pose any problem;
mod_perl, however, isn't exactly equivalent to a batch script.

The problem is: Where in threaded mod_perl do I get a chance to do fixup
work, like closing handles?

I tried PerlChildInitHandler/PerlChildExitHandler, only to notice
that I then run into the issues of nothing being global, unless using
threads::shared, which caused even more weird issues (remember the
PerlChildInitHandler seemingly *blocking* on $DbEnv->open).

So I went back to Mark's and Craig's initial recommendation of using
package level my-variables (which are local to the thread) to hold the
handles, initialize them lazily, and then using them as long as needed.

The issue *seems* to be (I could be wrong) that in order for Berkeley to
be happy, I'd have to be able to have some fixup code run the moment a
thread decides it has to leave the stage, so the handles stored in the
package-level my-variables can be closed in a controlled fashion.

Can I have code run at that instant?

Or any other ideas what's going on?

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
mackenna@animalhead.com schrieb:
> Yes it is now Abend again.  Timewise this is like relationships I
> have had with Philips (now NXP) European colleagues...

And a new morning again, and it goes round and round ...

> On Jan 18, 2009, at 10:56 AM, Michael Ludwig wrote:
>
>> mackenna@animalhead.com schrieb:
>>
>> Thanks for replying again. What you've outlined above is what I had
>> implemented following Mark's suggestion in his first reply to my
>> initial post. Unfortunately, this quite reliably produces SEGVs, so I
>> think this usage is not aligned with the Berkeley interface design. I
>> don't know in which way this is wrong - I guess I'll have to ask the
>> Berkeley folks in order to maybe clarify this.
>>
> I was using berkeleyDB 4.?, but without the fancy threading stuff you
> mention below.

The BerkeleyDB module is based on the C interface while Sleepycat::Db is
based on the C++ extension to the C interface, as far as I can tell.

> Since you can't share a tied variable (sharing uses the tying
> mechanism), while I was futzing with threading, I tried to share the
> underlying filehandle and use the function interface.  But that gave
> lots of SEGVs so I quit sharing anything about the DBs between
> threads, which got rid of the SEGVs.

Good. So you got the BerkeleyDB module working on a threading worker MPM
by avoiding any attempt at inter-thread sharing.

I'm still getting SEGVs trying to do the same with Sleepycat::DbXml.

>>> What this means is that each thread must open the db's for itself.
>>> The amount of data stored for each open DB connection, times
>>> THREADS_PER_CHILD times the number of Apache children at any
>>> given point, makes for some memory.  But
>>>
>>> 1) the separate connections help the DB package be thread-safe,
>>
>> So if coding the handler as above means that each thread, having its
>> own global variables, opens its own handle to the Berkeley
>> environment, I shouldn't need the DB_THREAD flag (which, according to
>> the Berkeley documentation, "[causes] the DbEnv handle returned by
>> DbEnv::open to be free-threaded; that is, concurrently usable by
>> multiple threads in the address space." This flag is needed if any
>> handle is used by more than one thread (or process) concurrently. So
>> it shouldn't be needed if the handler is coded as above and there
>> isn't any concurrent access elsewhere.
>>
> I have not worked with this flag but from your words it sounds right.
>
>>> 2) the first-used threads keep getting re-used in preference to
>>>    threads not yet used.
>>
>> I noticed this is indeed what seems to happen - whether by chance or
>> as a feature, I don't know.
>
> You can find it described proudly as a memory-minimization feature in
> some of the Apache docs about worker and/or event.

Does anyone know where this is? Haven't found it here:

http://httpd.apache.org/docs/2.2/mod/worker.html

>>> 3) if you consider each thread as more or less equivalent to a
>>>    child process in prefork, your total memory requirement is less.
>>
>> From perldoc perlthrtut: "In this model each thread runs in its own
>> Perl interpreter, and any data sharing between threads must be
>> explicit." This does not sound to me as if there is a significant
>> advantage over spawning child processes, at least not on UNIX.
>
> Start a prefork Apache2 (if you want, let it run a while to give the
> children a chance to grow.  Note the (largest or average) size of
> child processes.
>
> Start a worker or event Apache, let it run similarly (or not at all if
> you're impatient.  Divide the (largest or average) size of child
> processes by THREADS PER CHILD.  My point was that this number will be
> much smaller than the process size with prefork.

Okay, I see. More concurrency bang for the memory buck, despite certain
restrictions, if my understanding is correct, of the Perl threading
implementation.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
mackenna@animalhead.com schrieb:
> What I did to get worker and event working under FreeBSD 6.3,
> was to eliminate the child_init handler, and at the start of
> the response handler do something like
>
> my $env;
>
>  sub handler { # this is the response handler
>     my ($r) = @_;
>     if (!$env) {
>         $env = DbEnv->new;
>         $env->open( $envdir, Db::DB_USE_ENVIRON | Db::DB_THREAD);
>         ...
>        }
>     ...
>  }

Guten Abend, Craig!

Thanks for replying again. What you've outlined above is what I had
implemented following Mark's suggestion in his first reply to my initial
post. Unfortunately, this quite reliably produces SEGVs, so I think this
usage is not aligned with the Berkeley interface design. I don't know in
which way this is wrong - I guess I'll have to ask the Berkeley folks in
order to maybe clarify this.

> What this means is that each thread must open the db's for itself.
> The amount of data stored for each open DB connection, times
> THREADS_PER_CHILD times the number of Apache children at any
> given point, makes for some memory.  But
>
> 1) the separate connections help the DB package be thread-safe,

So if coding the handler as above means that each thread, having its own
global variables, opens its own handle to the Berkeley environment, I
shouldn't need the DB_THREAD flag (which, according to the Berkeley
documentation, "[causes] the DbEnv handle returned by DbEnv::open to be
free-threaded; that is, concurrently usable by multiple threads in the
address space." This flag is needed if any handle is used by more than
one thread (or process) concurrently. So it shouldn't be needed if the
handler is coded as above and there isn't any concurrent access
elsewhere.

> 2) the first-used threads keep getting re-used in preference to
>    threads not yet used.

I noticed this is indeed what seems to happen - whether by chance or as
a feature, I don't know.

> 3) if you consider each thread as more or less equivalent to a
>    child process in prefork, your total memory requirement is less.

 From perldoc perlthrtut: "In this model each thread runs in its own Perl
interpreter, and any data sharing between threads must be explicit."
This does not sound to me as if there is a significant advantage over
spawning child processes, at least not on UNIX.

Hmm. Not sure what to make of this threaded Perl.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by ma...@animalhead.com.
What I did to get worker and event working under FreeBSD 6.3,
was to eliminate the child_init handler, and at the start of
the response handler do something like

> my $env;

  sub handler { # this is the response handler
	my ($r) = @_;
	if (!$env) {
>>     	$env = DbEnv->new;
>>     	$env->open( $envdir, Db::DB_USE_ENVIRON | Db::DB_THREAD);
		...
>        }
	...
  }

What this means is that each thread must open the db's for itself.
The amount of data stored for each open DB connection, times
THREADS_PER_CHILD times the number of Apache children at any
given point, makes for some memory.  But

1) the separate connections help the DB package be thread-safe,
2) the first-used threads keep getting re-used in preference to
    threads not yet used.
3) if you consider each thread as more or less equivalent to a
    child process in prefork, your total memory requirement is less.

Guten abend (in meiner Zeitzone),
cmac
www.animalhead.com


On Jan 16, 2009, at 11:34 AM, Michael Ludwig wrote:

> Mark Hedges schrieb:
>>>
>>> *** glibc detected *** free(): invalid pointer: 0x084e6a14 ***
>>
>> If this didn't work in a response handler, I'd guess there's
>> something else wrong.  That's the kind of error you get when
>> you use things under threads that are not thread-safe in
>> some way, I remember that sort of thing trying to use
>> List::MoreUtils::natatime() under threads.  (But the rest of
>> that library works.)  It's a sad state of affairs that a
>> large chunk of CPAN doesn't play nice with threads, and
>> there's usually not any way of knowing which library is
>> causing the problem.  It might not even be the oracle
>> thingy, maybe something else you're loading.
>
> It may have been XML::LibXML, XML::LibXSLT, or - most probably - my
> lack of understanding of the worker MPM and threaded Perl.
>
> I made some progress and got it working using package-level
> my-variables to cache the Berkeley handles.
>
> Still, concurrent access quite reliably manages to produce SEGVs.
> This may have something to do with with not finalizing the handles
> properly.
>
> So I think I have to use PerlChildInitHandler and  
> PerlChildExitHandler.
>
> I spent some time wondering why the variables I initialized in the
> PerlChildInitHandler Eumel::A::child_init were undefined when
> accessed from the PerlResponseHandler Eumel::B::handler, regardless
> of whether they were declared with our or with my and then returned
> by a function.
>
> I thought that moving the child_init method into the same package
> as the handler and having it initialize package level my-variables
> or our-variables might do the trick. But it doesn't.
>
> Looks like the variables that the PerlChildInitHandler sees are
> different beasts than those the PerlResponseHandler sees, even
> though they are textually identical.
>
> Turns out under the threaded worker MPM things behave differently:
>
> | Global variables are only global to the interpreter in which
> | they are created. Other interpreters from other threads
> | can't access that variable. Though it's possible to make
> | existing variables shared between several threads running in
> | the same process by using the function
> | threads::shared::share().
>
> http://perl.apache.org/docs/2.0/user/coding/ 
> coding.html#Shared_Variables
>
> Should have RTFM before.
>
> I haven't used threads to this date, nor threads::shared. Looks like
> I'd have to share the Berkeley handles in order for it to *maybe*
> work.
>
> Now here's a funny one.
>
> use threads;
> use threads::shared;
>
> our $env;
>
> sub child_init {
>     share($env);
>     $env = DbEnv->new;
>     $env->open( $envdir, Db::DB_USE_ENVIRON | Db::DB_THREAD);
>     print STDERR "child_init: Umgebung geoeffnet\n";
>     ...
> }
>
> I never see the log message - the PerlChildInitHandler thing is
> hanging. Which bizarrely doesn't stop the PerlResponseHandler, by
> the way.
>
> If I uncomment "use threads", the threads::shared stuff conveniently
> turns into no-ops, and I get back the old behaviour, which is that
> the PerlChildInitHandler and the PerlResponseHandler do not see the
> same variables in spite of their being textually identical.
>
> I'm at my wit's end. Any clues?
>
> Michael Ludwig


Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Mark Hedges schrieb:
>>
>> *** glibc detected *** free(): invalid pointer: 0x084e6a14 ***
>
> If this didn't work in a response handler, I'd guess there's
> something else wrong.  That's the kind of error you get when
> you use things under threads that are not thread-safe in
> some way, I remember that sort of thing trying to use
> List::MoreUtils::natatime() under threads.  (But the rest of
> that library works.)  It's a sad state of affairs that a
> large chunk of CPAN doesn't play nice with threads, and
> there's usually not any way of knowing which library is
> causing the problem.  It might not even be the oracle
> thingy, maybe something else you're loading.

It may have been XML::LibXML, XML::LibXSLT, or - most probably - my
lack of understanding of the worker MPM and threaded Perl.

I made some progress and got it working using package-level
my-variables to cache the Berkeley handles.

Still, concurrent access quite reliably manages to produce SEGVs.
This may have something to do with with not finalizing the handles
properly.

So I think I have to use PerlChildInitHandler and PerlChildExitHandler.

I spent some time wondering why the variables I initialized in the
PerlChildInitHandler Eumel::A::child_init were undefined when
accessed from the PerlResponseHandler Eumel::B::handler, regardless
of whether they were declared with our or with my and then returned
by a function.

I thought that moving the child_init method into the same package
as the handler and having it initialize package level my-variables
or our-variables might do the trick. But it doesn't.

Looks like the variables that the PerlChildInitHandler sees are
different beasts than those the PerlResponseHandler sees, even
though they are textually identical.

Turns out under the threaded worker MPM things behave differently:

| Global variables are only global to the interpreter in which
| they are created. Other interpreters from other threads
| can't access that variable. Though it's possible to make
| existing variables shared between several threads running in
| the same process by using the function
| threads::shared::share().

http://perl.apache.org/docs/2.0/user/coding/coding.html#Shared_Variables

Should have RTFM before.

I haven't used threads to this date, nor threads::shared. Looks like
I'd have to share the Berkeley handles in order for it to *maybe*
work.

Now here's a funny one.

use threads;
use threads::shared;

our $env;

sub child_init {
     share($env); 

     $env = DbEnv->new;
     $env->open( $envdir, Db::DB_USE_ENVIRON | Db::DB_THREAD);
     print STDERR "child_init: Umgebung geoeffnet\n";
     ...
}

I never see the log message - the PerlChildInitHandler thing is
hanging. Which bizarrely doesn't stop the PerlResponseHandler, by
the way.

If I uncomment "use threads", the threads::shared stuff conveniently
turns into no-ops, and I get back the old behaviour, which is that
the PerlChildInitHandler and the PerlResponseHandler do not see the
same variables in spite of their being textually identical.

I'm at my wit's end. Any clues?

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Mark Hedges schrieb:
>
> On Thu, 15 Jan 2009, Michael Ludwig wrote:
>
>> PerlRequire /home/milu/www/eumel/startup.pl
>> PerlChildInitHandler Eumel::Gurke::bla;
>> PerlChildExitHandler Eumel::Gurke2::bla;
>
> No trailing semicolons?

That was a copy and paste error when composing the mail. But this is an
important point, as there won't be any warning about this - it will
silently fail, regardless of whether or not the module has been loaded.

> Probably if you first do PerlLoadModule Foo::Bar it will
> fail and inform you

It definitely will fail and output an error message. So it's crucial
to PerlLoadModule the module first, else even a correctly specified
PerlChildExitHandler won't be installed.

Thanks a lot, Mark. More on this later.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Mark Hedges <he...@scriptdolphin.org>.

On Thu, 15 Jan 2009, Michael Ludwig wrote:

> PerlRequire /home/milu/www/eumel/startup.pl
> PerlChildInitHandler Eumel::Gurke::bla;
> PerlChildExitHandler Eumel::Gurke2::bla;

No trailing semicolons?

Probably if you first do PerlLoadModule Foo::Bar it will
fail and inform you

> >  my $container = XmlContainer->new('/path/to/whatever.xml');
> >
> >  sub handler {
> >      my ($r) = @_;
> >      $container->open(Db::DB_CREATE);
> >      $container->put...
> >      # ...
> >      $container->close();  # ?? not sure how it works
> >  }
>
> I tried this style of initialization. It doesn't solve
> things. Depending on how exactly I code this, I get either
> a SEGV on startup attempt or when attempting concurrent
> access to the application. I also saw this:
>
> *** glibc detected *** free(): invalid pointer: 0x084e6a14 ***

If this didn't work in a response handler, I'd guess there's
something else wrong.  That's the kind of error you get when
you use things under threads that are not thread-safe in
some way, I remember that sort of thing trying to use
List::MoreUtils::natatime() under threads.  (But the rest of
that library works.)  It's a sad state of affairs that a
large chunk of CPAN doesn't play nice with threads, and
there's usually not any way of knowing which library is
causing the problem.  It might not even be the oracle
thingy, maybe something else you're loading.

Mark

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Mark Hedges schrieb:
> On Wed, 14 Jan 2009, Michael Ludwig wrote:
>
>> I want to build a mod_perl2 application using
>> Sleepycat::DbXml. This is
>>
>> However, I don't know how to do this. Currently, I'm
>> trying to set up things in startup.pl (loaded via
>> PerlPostConfigRequire), store the database environment
>> handle in a global package variable, and access that from
>> the PerlContentHandler.
>
> This probably won't work since the filehandle cannot be
> shared among mod_perl children.

Thanks for your help, Mark. I'm not sure it's a plain filehandle,
but probably your remark applies anyway.

> Your startup.pl script is running before the root apache
> process forks into the child processes.  Scalars, lists and
> hashes will be cloned, but file handles won't, they have to
> be opened.

Cloning might not be what the DbEnv, XmlManager and XmlContainer
objects need, or tolerate.

> Or, you can use it like this in a handler, essentially the
> same thing [as the PerlChildInitHandler]:
>
>  package YourResponseHandler;
>  use strict;
>  use warnings FATAL => 'all';
>
>  use SleepyCat::DbXml qw(simple);
>
>  # is this the way it works?  i trolled for it...
>  # where is the manual page??  ugh

Probably not all in place yet.

>  my $container = XmlContainer->new('/path/to/whatever.xml');
>
>  sub handler {
>      my ($r) = @_;
>      $container->open(Db::DB_CREATE);
>      $container->put...
>      # ...
>      $container->close();  # ?? not sure how it works
>  }

I tried this style of initialization. It doesn't solve things.
Depending on how exactly I code this, I get either a SEGV on
startup attempt or when attempting concurrent access to the
application. I also saw this:

*** glibc detected *** free(): invalid pointer: 0x084e6a14 ***

> But it's not clear how much time you're going to save. I
> don't know how the thing works.  You may or may not be able
> to call open() from package scope because of locking, are
> locks on the sleepycat database serial?

No, it supports multi-process and multi-threaded access.

Good news: I've been offered help. I'll report back once I'll
have attained a successful configuration, or be convinced that
this simply won't work.

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by ma...@animalhead.com.
I started with 'worker', and it had similar performance to
what I saw thereafter with 'event'.  Actually slightly better,
perhaps because 'event' gained no advantage from KeepAlive
because each test process stayed locked to one connection.

'worker' and 'event' were experiments that I decided to
terminate, in part because I was so annoyed with Perl ithreads
after I had laboriously eliminated many global variables from
my mod_perl scripts that didn't want to be shared.  This
activity was based on the "Programming Perl" description of
"threads 5.005".  Oh well, now the code should be better set
for the future.

Best Regards,
cmac
www.animalhead.com


On Jan 16, 2009, at 8:14 AM, Adam Prime wrote:

> mackenna@animalhead.com wrote:
>> Apache is forgiving/robust about specifying nonexistent phase
>> handlers in httpd.conf and inserts thereto.
>> I was using event, and had to let each thread open its own DBs
>> based on an undefined global.  Then I found that the identical
>> traffic-test exerciser could get 66 files/second back in event
>> and 1500/second in prefork.  This is under FreeBSD 6.3.
>> No doubt I had something wrong or "tunable" in event, but at
>> that point I gave up and will wait for the simple MPM.
>
> The event MPM is also currently referred to as being 'experimental'  
> by the httpd folks.  have you tried worker instead?
>
> Adam


Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Adam Prime <ad...@utoronto.ca>.
mackenna@animalhead.com wrote:
> Apache is forgiving/robust about specifying nonexistent phase
> handlers in httpd.conf and inserts thereto.
> 
> I was using event, and had to let each thread open its own DBs
> based on an undefined global.  Then I found that the identical
> traffic-test exerciser could get 66 files/second back in event
> and 1500/second in prefork.  This is under FreeBSD 6.3.
> 
> No doubt I had something wrong or "tunable" in event, but at
> that point I gave up and will wait for the simple MPM.

The event MPM is also currently referred to as being 'experimental' by 
the httpd folks.  have you tried worker instead?

Adam

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
mackenna@animalhead.com schrieb:
> Apache is forgiving/robust about specifying nonexistent phase
> handlers in httpd.conf and inserts thereto.

Thanks. I noticed. :-)

> I was using event, and had to let each thread open its own DBs
> based on an undefined global.  Then I found that the identical
> traffic-test exerciser could get 66 files/second back in event
> and 1500/second in prefork.  This is under FreeBSD 6.3.
>
> No doubt I had something wrong or "tunable" in event, but at
> that point I gave up and will wait for the simple MPM.

Thanks. I'll try prefork if I can't get worker to work.

Michael

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by ma...@animalhead.com.
Apache is forgiving/robust about specifying nonexistent phase
handlers in httpd.conf and inserts thereto.

I was using event, and had to let each thread open its own DBs
based on an undefined global.  Then I found that the identical
traffic-test exerciser could get 66 files/second back in event
and 1500/second in prefork.  This is under FreeBSD 6.3.

No doubt I had something wrong or "tunable" in event, but at
that point I gave up and will wait for the simple MPM.

Best Regards,
cmac
www.animalhead.com


On Jan 15, 2009, at 7:49 AM, Michael Ludwig wrote:

> Mark Hedges schrieb:
>> Probably what you're thinking of is a PerlChildInitHandler
>> so that each mod_perl child process does your connection for
>> you when the child process first forks.
>
> Yes, that's what I thought. But then I noticed that the
> PerlChildInitHandler I set up is ignored. Apache configuration
> snippet:
>
> PerlRequire /home/milu/www/eumel/startup.pl
> PerlChildInitHandler Eumel::Gurke::bla;
> PerlChildExitHandler Eumel::Gurke2::bla;
>
> These two classes do not even exist, but Apache starts fine.
> When I replace those dummy entries with existing classes, I can
> see they're ignored because I don't see the log output I should
> see.
>
> Is is possible these are ignored for the worker MPM?
>
> | In the prefork MPM this phase is useful for initializing
> | any data structures which should be private to each process.
>
> http://perl.apache.org/docs/2.0/user/handlers/ 
> server.html#C_PerlChildInitHandler_
>
> It is unclear to me whether this applies to the prefork MPM
> exclusively. Does anyone know?
>
> Michael Ludwig


Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Michael Ludwig <ml...@as-guides.com>.
Mark Hedges schrieb:
> Probably what you're thinking of is a PerlChildInitHandler
> so that each mod_perl child process does your connection for
> you when the child process first forks.

Yes, that's what I thought. But then I noticed that the
PerlChildInitHandler I set up is ignored. Apache configuration
snippet:

PerlRequire /home/milu/www/eumel/startup.pl
PerlChildInitHandler Eumel::Gurke::bla;
PerlChildExitHandler Eumel::Gurke2::bla;

These two classes do not even exist, but Apache starts fine.
When I replace those dummy entries with existing classes, I can
see they're ignored because I don't see the log output I should
see.

Is is possible these are ignored for the worker MPM?

| In the prefork MPM this phase is useful for initializing
| any data structures which should be private to each process.

http://perl.apache.org/docs/2.0/user/handlers/server.html#C_PerlChildInitHandler_

It is unclear to me whether this applies to the prefork MPM
exclusively. Does anyone know?

Michael Ludwig

Re: Initializing Sleepycat::DbXml (Berkeley, Oracle) objects in startup.pl

Posted by Mark Hedges <he...@scriptdolphin.org>.

On Wed, 14 Jan 2009, Michael Ludwig wrote:

> I want to build a mod_perl2 application using
> Sleepycat::DbXml. This is
>
> However, I don't know how to do this. Currently, I'm
> trying to set up things in startup.pl (loaded via
> PerlPostConfigRequire), store the database environment
> handle in a global package variable, and access that from
> the PerlContentHandler.

This probably won't work since the filehandle cannot be
shared among mod_perl children.

Your startup.pl script is running before the root apache
process forks into the child processes.  Scalars, lists and
hashes will be cloned, but file handles won't, they have to
be opened.

Probably what you're thinking of is a PerlChildInitHandler
so that each mod_perl child process does your connection for
you when the child process first forks.

Or, you can use it like this in a handler, essentially the
same thing:

 package YourResponseHandler;
 use strict;
 use warnings FATAL => 'all';

 use SleepyCat::DbXml qw(simple);

 # is this the way it works?  i trolled for it...
 # where is the manual page??  ugh
 my $container = XmlContainer->new('/path/to/whatever.xml');

 sub handler {
     my ($r) = @_;
     $container->open(Db::DB_CREATE);
     $container->put...
     # ...
     $container->close();  # ?? not sure how it works
 }

But it's not clear how much time you're going to save. I
don't know how the thing works.  You may or may not be able
to call open() from package scope because of locking, are
locks on the sleepycat database serial? If so then every
mod_perl child process is going to be waiting for the others
to complete before they can establish their own locks and
there's no way around that. You'll have to balance how many
writes you expect to get with how many reads, maybe reading
actions do not require locks on the data file.

Mark