views:

781

answers:

3

I want an "eternal" process that goes through a MySQL table and spawns child processes. Pseudo code:

while(true)
    $rows = SELECT * FROM workers
    foreach($rows as $row){
         DELETE $row->id
         spawn_child($row->id)
    }
    sleep(5)
}
function spawn_child($id){
     $pid = pcntl_fork()
     if($pid <0){
         //err
     }elseif($pid == 0){
         //child
         exec("worker_program $id");
         exit();
     }elseif($pid > 0){
         //parent
     }
 }

The problem is that when the child process comes back from the worker_program and exits, it closes the apparently shared mysql-handle, so the parent process gets a "Msql server went away"-error.

How do I solve this? Is it a design fault?

How do I spawn and detach a process in PHP, without sharing any db resources etc, so that the child is free to exit?

(I have tried: setsid and forking again, calling workers with 'worker_program &' instead of forking in php, but that doesn't seem to work at all (weird?). I'm using PDO. Also the guys over at php.net say this behaviour is not a bug. This is on osx and php5.3 (and debian).)

Refs.:

php.net/bug: "Parent process lost MySQLi connection after child process gone"

Update/workaround

So I've finally found a way to deal with this. What works is to use popen to spawn the worker processes. That way it seems a completely "fresh" process is created, without any shares. I then let the child do the forking and detaching itself. So in the master process, instead of pcntl_fork or exec:

$p = popen("worker_program $arg","r");
sleep(1); //give it time to detach (won't work otherwise. Any other ideas?)
pclose($p);

And then in the worker program:

#!/usr/bin/env php
<?php

//fully detach from parent, as proposed by the 'gurus'
//(Why can't this be done with only one fork?)
if(pcntl_fork()) {
    exit();
}
posix_setsid();
if(pcntl_fork()) {
    exit();
}
...
+2  A: 

How about using a persistent MySQL connection. And closing it when you know you're done.

$conn = new mysqli("p:".$dbhost, $dbuser, $dbpass, $dbname);

$conn = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass,
    array(PDO::ATTR_PERSISTENT => true));
Ólafur Waage
I've tried both persistant and non-persistant. It does not help :/ I believe all resources (incl db handles, persistant or not) are shared to the child process when forked. When the child process exits, it just closes all of it handles, shared or not.
0scar
+1  A: 

How about if you unset the db handle explicitly when done in the child, so it won't be closed on child exit? The parent should retain it's handle link, so the link might not be closed.

Zak
That's a nice idea! Arhm, I'm gonna check it out on the next iteration through this code. I'm going with the popen stuff for now.. thx!
0scar
A: 

How about if you unset the db handle explicitly when done in the child, so it won't be closed on child exit? The parent should retain it's handle link, so the link might not be closed.

This doesn't help either. Parent mysql connection continues going away.((

yutas