Search code examples
phpmysqlpdodaemonpcntl

"MySQL gone away" in PHP daemon, using pcntl_fork


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


    }
}
}
?>

Solution

  • 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".