20

I have a foreach loop that forks within it. After the process forks, it accesses the database. I get an error:

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

The thing is, I'm connecting to the database after I've forked.

My question: Why would this be happening?

If this happens, am I actually accessing the database before forking? Will the child inherit DB connections?

(note: I can post code, but it's rather large as it's all in classes, which could be what is causing my confusion of when I'm accessing the DB. Another thing you should know is I'm using ZF.)

d-_-b
  • 6,555
  • 5
  • 40
  • 58
  • I haven't played wiht Zend Framework but I wonder if it keeps some sort of internal pooling of database connections. Or perhaps it's doing persistent connections? Other than that, children should not be inheriting db connections or anything else as they're different php processes. – Fanis Hatzidakis Sep 09 '10 at 14:43
  • Ack, I stand corrected. My above answer was based on intuition but not on personal experience as this has not been required yet. Reading more into it I see forked children do inherit their parent's db connection, and it is a known problem: http://www.php.net/manual/en/function.pcntl-fork.php#70721 – Fanis Hatzidakis Sep 09 '10 at 14:49
  • @Fanis - Can you turn your last comment into an answer so I can click the big green check? Thanks for digging this info out. I will not fork, instead I will exec a new process that will have it's own db connection. It will then fork, so as not to tie up the calling process, and then do it's work in the child, logging it's pid to a log that another cron-started-process will come along and check if it has been completed. Hmmmm... It might just work this time! Thanks! – d-_-b Sep 16 '10 at 13:52
  • 1
    Thanks, and good luck! On second thought, take a look at http://gearman.org as a viable option for when forking becomes so complicated. – Fanis Hatzidakis Sep 16 '10 at 14:02
  • gearman looks interesting. I may give it try on large installations. For now I want the code to be as portable as possible. So I don't want to introduce too many dependencies. Thanks! – d-_-b Sep 17 '10 at 03:15

5 Answers5

14

(comment --> answer per poster's request)

Reading more into it I see forked children do inherit their parent's db connection, and it is a known problem: http://php.net/manual/en/function.pcntl-fork.php#70721

Fanis Hatzidakis
  • 5,282
  • 1
  • 33
  • 36
  • I just thought I'd clarify: The reason why I got the error even though the child inherited the db connection is that I am forking a bunch of new processes that all need a db connection. – d-_-b Sep 17 '10 at 03:19
  • 1
    I'd like to add that this affects all resources, not just MySQL connections. For example all the sockets opened before forking will die if a child exits. So always create resources _after_ forking. The comment link in this answer is very valuable. – RedShift Mar 06 '21 at 21:08
9

This helped for me: http://www.electrictoolbox.com/mysql-connection-php-fork/

Especially mysql_connect($server, $username, $password, true);

David
  • 1,155
  • 9
  • 3
6

You can avoid closing connection when forked process exit, if you kill forked process with SIGKILL.

<?php
$dbh = new PDO('pgsql:host=localhost', $username, $password);
$pid = pcntl_fork();
if($pid == 0){
        register_shutdown_function(function(){
                posix_kill(getmypid(), SIGKILL);
        });
        exit;
}
sleep(1);
$statement = $dbh->query('select 1');
var_dump($statement);

The reason of this behavior, that when PHP process is exit, than PHP sends to database server "Terminate connection" command. But socket will be closed by system only when all links to socket is closed. Using SIGKILL help us to avoid sending "Terminate connection" command to database server.

Tatikoma
  • 161
  • 1
  • 3
3

Except it is not a problem. It is the way pcntl_fork was designed. Any extension (as the documentation clearly states) that maintains it's own file descriptors will then have corrupted descriptors because all children an parents share the same file descriptors.

0

You need to close the MySQL connection on your parent process and then make a new connection for each child.

<?php
$dbh = new PDO('pgsql:host=localhost', $username, $password);
$pid = pcntl_fork();
if(!$pid){
        // make new connection
        $newConnection = new PDO('pgsql:host=localhost', $username, $password);
        // do something in the child process.
        exit;
}else{ 
        // parent node
        $dbh = null; // close PDO connection
}
Adam
  • 6,447
  • 1
  • 12
  • 23