Forking and database connections

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey all,

I'm writing a PHP script that queries a database for some records, splits
the records into N number of groups of equal size, and then creates N number
of forks where each child handles one of the groups.  During the execution
of each of the child processes, I'd like the parent process to update the
status of the job in the database.

The problem is regarding my database connection pre- and post- fork.  After
reading the pcntl_fork() page on the PHP manual, I realize that the child
process inherits the file descriptor, and if the child process closes the
connection, then it is closed in the parent process.  So for each child
process (because I have more than one), I reinitialize the database link.  I
also reinitialize the database link for the parent process immediately after
the fork.

However, when a child process finishes, it seems like the database link that
I reinitialized in the parent process also disconnects.  I thought a fork
copied the entire heap, and therefore would make two copies of the object
instances that would remain segmented for the life of the processes.
Changes made to one copy of the heap wouldn't affect others.  However, this
doesn't seem to be the case.

So at this point, my workaround is to wait until all of the child processes
are finished, then re-initialize the database link, and give an updated
status message at the end rather than incrementally as child processes
finish.

Here's some proof-of-concept code that explains what I mean:

<?php

/* Include PEAR::DB */
require_once('DB.php');

# Database table definition
# -------------------------
# CREATE TABLE `logs` (
#      `message` VARCHAR(128) NOT NULL
# );

/* Create the initial database connection for the parent process */
$dsn = 'mysql://test:test@localhost/testdb';
$db = DB::connect($dsn);
if ( PEAR::isError($db) ) {
        die($db->getMessage() . "\n");
}

/* This will be the common SQL statement for all inserts */
$sql = "INSERT INTO `logs` (`message`) VALUES (?);";
$stmt = $db->prepare($sql);

/* Perform a DB update */
$data = array('Started parent process');
$db->execute($stmt, $data);

/* Create the child processes */
$childPids = array();
for ( $i = 0; $i < 5; $i++ ) {
        $pid = pcntl_fork();
        if ( $pid == -1 ) {
                die("\nUnable to fork!\n");
        } else if ( $pid ) {
                /* Parent process */
                echo "Child process $pid created\n";
                array_push($childPids, $pid);
        } else {
                /* Child process */
                $myPid = posix_getpid();

                /* Create a new database connection for the child process */
                $db = DB::connect($dsn);
                if ( PEAR::isError($db) ) {
                        die("\nChild process $myPid: " . $db->getMessage() .
                            "\n" . $db->getDebugInfo() . "\n");
                }

                $data = array("Child process $myPid");
                $stmt = $db->prepare($sql);
                $db->execute($stmt, $data);

                /* Add some latency for testing purposes */
                sleep(5);
                exit;
        }
}

/* Create a new database connection for the parent process */
$db = DB::connect($dsn);
if ( PEAR::isError($db) ) {
        die("\nParent process: " . $db->getMessage() . "\n" .
            $db->getDebugInfo() . "\n");
}

/* Wait for the children to finish */
foreach ( $childPids as $pid ) {
        $data = array("Parent process waiting on child process $pid");
        $db->execute($stmt, $data);
        pcntl_waitpid($pid, $status);
        $data = array("Child process $pid is finished");
        $db->execute($stmt, $data);
}

$data = array("Parent process is finished");
$db->execute($stmt, $data);

?>

The command-line output of this code:

$ php forking-proof-of-concept.php
Child process 27012 created
Child process 27013 created
Child process 27014 created
Child process 27015 created
Child process 27016 created

Child process 27016: DB Error: unknown error
 [nativecode=2013 ** Lost connection to MySQL server during query] **
mysql://test:test@localhost/testdb

And finally the database entries after running the code:

mysql> select * from logs;
+------------------------+
| message                |
+------------------------+
| Started parent process |
| Child process 27012    |
| Child process 27013    |
| Child process 27014    |
| Child process 27015    |
+------------------------+
5 rows in set (0.00 sec)

Any help in understanding this is appreciated!

Thanks!
Chris

-- 
PHP Database Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [PHP Users]     [Postgresql Discussion]     [Kernel Newbies]     [Postgresql]     [Yosemite News]

  Powered by Linux