0scar
0scar

Reputation: 3200

Spawn and detach PHP process without sharing any db-resources so that the child can exit?

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();
}
...

Upvotes: 2

Views: 3205

Answers (3)

ekerner
ekerner

Reputation: 5848

None of those worked for me. I did this:

// php execl type behaviour, execute and leave ...
// the args may be passed to $command or $args
// [email protected]
function execl($command, $args = array())
{
  $command = escapeshellarg($command);
  foreach ($args as $arg)
    $command .= ' ' . escapeshellarg($arg);
  exec($command . ' 2>/dev/null >&- /dev/null &');
}

Upvotes: 0

Zak
Zak

Reputation: 25237

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.

Upvotes: 1

&#211;lafur Waage
&#211;lafur Waage

Reputation: 70031

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));

Upvotes: 2

Related Questions