RE: Forking and database connections

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

 



Good point.  Anything (children or parent) sharing the connection will get
it closed on them.  I was looking up persistent connections, then I thought
you could just address the way you used the pcntl_* functions.

I use mysql_* directly (where mysql_connect "Opens or reuses a connection to
a MySQL server."), so you may need to look into PEAR::DB for persistent
connections.

Where in your code does it say you are using persistent connections?  I
haven't had the need to use them yet.

For instance mysql_pconnect (the persistent counterpart) says "Second, the
connection to the SQL server will not be closed when the execution of the
script ends."

If you are using the PEAR::DB persistent connection as documented, then I'd
open a bug report.  Again, I haven't used them.



Regards,
Dwight
> -----Original Message-----
> From: Chris Verges [mailto:chrisv@xxxxxxxxxxxxxxxxxx]
> Sent: Monday, April 23, 2007 12:01 PM
> To: Dwight Altman; php-db@xxxxxxxxxxxxx
> Subject: Re:  Forking and database connections
> 
> 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