ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

I have a parent process that creates a DBI::mysql connection (held in $dbh) to database D, executes some SQL requests, forks some children, (each of which want to execute SQL requests to the same database), and then continues to execute more SQL. The children of course inherit $dbh, but I'm not having much luck from then on.

Someone suggested executing $dbh->{InactiveDestroy} = 1; before forking, to prevent the children's exits from clobbering the shared connection. But I get from Sharing a database handle over multiple processes (dated 2001) that you shouldn't expect a database handle to survive the fork.

So what approach should I take? It sounds like I should disconnect $dbh before I fork (so that no children inherit my connection); have the children each connect afresh; and, back in the parent, *reconnect* to D and continue running.

Or perhaps I should leave the connection open, issue $dbh->{InactiveDestroy} = 1; before any forking, and have the children simply create their own connections in $dbh_child. When they exit, the $dbh_child connection will be cleaned up, and the inherited $dbh connection will be untouched.

Thanks for any help!

Replies are listed 'Best First'.
Re: Sharing a database connection across fork()
by Anonymous Monk on Sep 11, 2014 at 09:03 UTC

    See DBI, fork, and clone. (note the use of the clone function there). Other than that I've found conflicting information.

    Even though I've found several places that say to set InactiveDestroy in the parent, I've also found several places that say instead to set it in each child, such as this PerlMonks post, as well as the DBI documentation on the option AutoInactiveDestroy.

    It does seem safer to disconnect the handle in the parent and reconnect in the children (see e.g. this PerlMonks post). Also there's this post (pretty old) on a MySQL mailing list, which says "In other words, whether a connection can be used in a forked child, will almost definitely vary between database drivers and even versions."

      To summarize, here's what http://www.perlmonks.org/?node_id=594175 demonstrated, and what worked for me:

      (Parent) establish database connection in $dbh. (Parent) Fork. (Child) $dbh_child = $dbh->connect(inherited connection parameters); (Child) $dbh->{InactiveDestroy} = 1; (Child) undef $dbh; (Child) # proceed to use $dbh_child (Parent) # proceed to use $dbh (Child) exit; (Parent) # continue using $dbh
      (Note that I edited the above to replace the clone() call with a connect() call, as recommended by wrog.) Thanks to all!

        Current DBI docs claim use of clone() without an argument is deprecated (because there's some question of which attributes should be copied over, which may change, or something). No idea how likely they'll go through with it.

        Also, if the original connection specs are available then there's no reason not to just do a fresh connect() in the child (since that's what clone does anyway), i.e., you're not actually saving anything by inheriting it from the parent.

Re: Sharing a database connection across fork()
by wrog (Friar) on Sep 11, 2014 at 14:31 UTC
    Things to keep in mind:
    1. DESTROY on a connection will, for many db drivers, do Something Bad to the connection that renders it useless in all processes that it's been copied to. Setting $dbh->{InactiveDestroy}=1 in a particular process prevents that Something Bad from happening in that process. However the Something Bad is most likely something that needs to happen once.

      Therefore, you want to set $dbh->{InactiveDestroy} in every process except whichever one is going to be the last one to be holding onto the connection (usually, that's the parent but not always; depends on how your program actually works)

      Or you want to set $dbh->{AutoInactiveDestroy} as soon as you create it, provided that the process that's creating is also going to be last/only one to be using it, which is often the case (but again not always). And this option is also best if there can be forks happening behind your back.

    2. And in the vast majority of case where I said "last/only", you really want that to be "only", i.e., you almost never want to be actually using (reading/writing) the connection from more than one process. Not if the database driver has any kind of state that needs to be kept on the client side of the connection. Because if that's so, then once you do something in one process, that will likely change the state and from then on attempts to use the connection from another process will fail.

    3. And even if that's not the case and you actually can do stuff safely from more than one process (say, because, you're on Windows, and fork isn't actually fork, and the DB driver author took the trouble to write a CLONE routine that does the Right Thing with all of the client-side state), you still need to be doing synchronization so that no two process are attempting to read or write the same connection at the same time.
Re: Sharing a database connection across fork()
by locked_user sundialsvc4 (Abbot) on Sep 11, 2014 at 12:38 UTC

    Expect that each child must establish its own database connection, using credential strings provided (or inherited) by the parent.   A “database handle” is, after all, representative of an internal data-structure which represents not only the connection but the entire context of that connection (current record position, prepared query, and so on), all of which your various threads are attempting to use – along with the parent – with no provision being made to synchronize those activities.   Conflcts, and crashes, are inevitable.

    It will also be particularly important that there not be too-many threads, and that they are persistent:   having created their (individual ...) database connection, they should keep it for a long while and do many units of work in succession through it.