Re: Forking and database connections

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

 



Hey Dwight,

Thanks for the replies!  When the first child thread closes, all of the DB
connections seem to close.  That is, the $db->execute() statements will fail
in the child threads after the first child thread closes.  It's almost as
though separate connections are not being made, but they are instead sharing
the same connection.  This would make sense if persistent connection pooling
was enabled, but I assumed there to be a "garbage collection" mechanism in
place to avoid prematurely closing a persistent connection if multiple
resources are using it.

Again, thanks for the help!  Any other ideas on what might be wrong, or
should I open a bug against it?

Thanks!
Chris


On 4/23/07 8:12 AM, "Dwight Altman" <dwight@xxxxxxxxxxxx> wrote:

> Actually I suppose you need to loop with foreach to wait on all children
> before attempting any $db->execute, then after your foreach loop, get your
> $db and execute.
> 
> Regards,
> Dwight
> 
>> -----Original Message-----
>> From: Dwight Altman [mailto:dwight@xxxxxxxxxxxx]
>> Sent: Monday, April 23, 2007 10:09 AM
>> To: 'php-db@xxxxxxxxxxxxx'
>> Subject: RE:  Forking and database connections
>> 
>> 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@xxxxxxxxxxxxxxxxxx]
>>> Sent: Saturday, April 21, 2007 12:21 PM
>>> To: php-db@xxxxxxxxxxxxx
>>> Subject:  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

-- 
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