[Zope] zope fork in external method - mysql connection dropped

Daniel Lopez zope-list at sevanta.com
Wed Oct 18 16:13:40 EDT 2006

I spoke too soon.  It was working, but consistent with the Curse of Fork, 
the mysql error eventually started showing up inconsistently.  At first I 
thought it might be dependent on zope's debug mode, but it started showing 
up either way.

So I did what I didn't want to do: split out the 'grandchild' code into a 
command-line script... with the resulting basic structure:

>> external_method.py:
#do some zope stuff here
rv = os.system("shell_script.sh")
if rv:
   #handle error here, such as command not found
return RESPONSE.redirect("wait_page")

>> shell_script.sh
#do some shell stuff here
python_script.py &
exit 0

>> python_script.py
#all the grandchild code here

My understanding of this is that (1) os.system creates a shell in a new 
process [without copying the parent's stack and files?], (2) the ampersand 
line in the shell script creates a grandchild process, and (3) the shell 
script exits, orphaning the python_script and allowing the grandparent to 

Maybe it's a little roundabout, but this seems to be cleaner and more 
reliable so far.  Comments?


On Tue, 17 Oct 2006, Daniel Lopez wrote:

> You're good, my friend.  I was hoping something like this existed, and so 
> far, it seems to be working exactly as desired.
> One note for others: it's actually os._exit() not sys._exit()
> Re: the other suggestions:
> (1) I was already using runzope
> (2) subprocess module: I'm using an older version of zope and python, and 
> can't currently risk upgrading core components.
> For true cleanliness, in order to avoid the use of fork(), I could try to 
> pull out the grandchild's code into a self-standing script that could get 
> spawned by the grandparent, but that would require a great deal of work, as 
> the grandchild makes a few calls back into zope, along with using some 
> information in the request, which I'd need to pass to the standalone script. 
> I'm happy the lazier approach worked.
> Thanks to all...
> -D
> On Tue, 17 Oct 2006, Dieter Maurer wrote:
>> Daniel Lopez wrote at 2006-10-16 13:31 -0700:
>>> I made one tweak to the double-fork procedure, adding a waitpid call in 
>>> the
>>> grandparent process (the original zope thread) before it returns out of 
>>> the
>>> external method... the code then looked something like:
>>> [...prefork code up to here...]
>>> pid1 = os.fork()
>>> if pid1 > 0:
>>>    #grandparent waits for its child before returning
>>>    os.waitpid(pid1, 0)
>>>    return RESPONSE.redirect("wait_page")
>>> pid2 = os.fork()
>>> os.setsid()
>>> if pid2 > 0:
>>>    #child quits, orphaning grandchild
>>>    sys.exit(0)
>>> [...grandchild-only code after here...]
>>> The waitpid call seems to be preventing the zombies... this is good!
>>> (though if you find something bad about this approach, please do speak up)
>>> But in the process, a new bug was created, having to do with the MySQL
>>> connection.  I now receive a "Lost connection to MySQL server during
>>> query" error in what appears to be a final db flush from the grandparent's
>>> publish function (ZPublisher.Publish, line 104).  The good news is that
>>> the grandchild continues to do it's work, but the bad news is that the
>>> user receives an error page instead of the redirect to the "wait_page".
>> Maybe, the MySQL client library installed an "atexit" hook that closes
>> the database connection.
>> You may try "sys._exit(0)" (instead of "sys.exit(0)") to finish
>> the child. "_exit" is for purposes when you do not want standard
>> exit handling (such as flushing buffers and calling "atexit" hooks).
>> -- 
>> Dieter

More information about the Zope mailing list