| Dwight Altman 2007-04-23, 6:57 pm |
| http://php.he.net/manual/en/function.pcntl-fork.php says 'The reason for the
MySQL "Lost Connection during query"...' like what you concluded, although
they grab a new $db connection in the first for loop
"} else if ( $pid ) {" I bet a child closes the one you create after the for
loop while the parent is waiting in the foreach loop.
In your foreach when you wait for each child, can you $db->execute($stmt,
$data) AFTER you pcntl_waitpid($pid, $status) ? Just reverse the lines?
I think after the wait is when the child closes the connection (since I
suppose you are reusing the same connection), so it is already closed.
Regards,
Dwight
> -----Original Message-----
> From: Chris Verges [mailto:chrisv@cyberswitching.com]
> Sent: Saturday, April 21, 2007 12:21 PM
> To: php-db@lists.php.net
> Subject: [PHP-DB] Forking and database connections
>
> 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
|