1

I'm trying to retrieve database values using a PHP daemon and fork (using pcntlfork) an instance for each id that was retrieved.

Each fork is supposed to do some work and then alter the database value, so it won't be retrieved again.

However, when I fork a child and let it sleep for 10 seconds for example (realistic processing time), it seems the MySQL connection times out. How do I prevent this from happening? The try/catch doesn't seem to prevent the error.

#!/usr/bin/php
<?php
ini_set('memory_limit','256M');
gc_enable();

function sig_handler($signo) {
  global $child;
  switch ($signo) {
   case SIGCHLD:
     echo "SIGCHLD received\n";
     $child--;
  }
}

// install signal handler for dead kids
pcntl_signal(SIGCHLD, "sig_handler");

global $PIDS; $PIDS = array();
global $maxforks; $maxforks = 5;
global $child; $child = 1;

global $boot; $boot = true;
date_default_timezone_set('Europe/Brussels');

// figure command line arguments
if($argc > 0){
    foreach($argv as $arg){
        $args = explode('=',$arg);
        switch($args[0]){
            case '--log':
                $log = $args[1];
                break;
            case '--msgtype':
                $msgtype = $args[1];
                break;
        } //end switch
    } //end foreach
} //end if

// Daemonizen
$daemon_start = date('j/n/y H:i', time());
$pid = pcntl_fork();
if($pid == -1){
    return 1; // error
} else if($pid) {
    return 0;
} else {    
    while(true){

    try {
        $host = 'localhost';
    $dbname = 'bla';
    $dbuser = 'bla';
    $dbpass = 'bla';
        $db = new PDO('mysql:host='.$host.';dbname='.$dbname.';charset=utf8', $dbuser, $dbpass, array(PDO::ATTR_TIMEOUT => 2));
        //$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
    } catch (PDOException $e){
        echo $e->getMessage();
    }

    $read_messages = $db->query("SELECT * blablabla");
    while($read_message_row = $read_messages->fetch(PDO::FETCH_ASSOC)){
        $id = $read_message_row['id'];

        $pid1 = pcntl_fork();
        if ($pid1 == -1){
                die('could not fork');
        } else { #START ELSE COULD FORK
            $PIDS[$pid1] = $pid1; //KEEP TRACK OF SPAWNED PIDS
            if ($pid1){
                // parent
                if ($child++ >= $maxforks){
                        pcntl_wait($status);
                        $child++;
                }

                echo "Forking child with PID $pid1 voor $id.\n";

                //PARENT THREAD : $ch is a copy that we don't need in this thread
                // child forken
            } else {
                include_once "test_worker.php";
            } // einde child thread
        } //if-else-forked


    }
}
}
?>
halfer
  • 19,824
  • 17
  • 99
  • 186
driesken
  • 93
  • 9
  • In my experience database connections don't play well with fork(). I would try reading all the rows out, closing the PDO object, then doing the forks, then reopening the connection and processing results as they come in. – Austin Sep 09 '14 at 20:42

2 Answers2

3

Solution is simple. Connect (or reconnect) after forking.

Forked process is exact mirror of it's parent and shares resource handles. When one of your forked processes closes DB connection, it is closed for all other pocesses in the tree - even in the middle of the query. Then you get errors like "MySQL server has gone away" or "Lost connection to MySQL server during query".

Martin Strouhal
  • 1,174
  • 1
  • 12
  • 23
0

Don't use pcntl_fork() while you have database handles open. The database handle ends up shared by both child processes, leaving the connection in an inconsistent state.

In fact, avoid using pcntl_fork() at all if you can help it. Code using it will tends to be very fragile, and will not work correctly outside the command-line SAPI.