views:

298

answers:

3

I have a Perl script that reads a command file and restarts itself if necessary by doing:

myscript.pl:

exec '/home/foo/bin/myscript.pl';
exit(0);

Now, this works fine except for one issue. The thread that reads the command file does not have access to the DBI handle I use. And over a number of restarts I seem to build up the number of open mysql connections till I get the dreaded "Too Many Connections" error. The DBI spec says:

"Because of this (possibly temporary) restriction, newly created threads must make their own connections to the database. Handles can't be shared across threads."

Any way to close connections or perhaps a different way to restart the script?

+2  A: 

Use a flag variable that is shared between threads. Have the command line reading thread set the flag to exit, and the thread holding the DB handle release it and actually do the re-exec:

#!/usr/bin/perl

use threads;
use threads::shared;

use strict; use warnings;
my $EXIT_FLAG :shared;

my $db_thread = threads->create('do_the_db_thing');
$db_thread->detach;

while ( 1 ) {
    sleep rand 10;
    $EXIT_FLAG = 1 if 0.05 > rand or time - $^T > 20;
}

sub do_the_db_thing {
    until ( $EXIT_FLAG ) {
        warn sprintf "%d: Working with the db\n", time - $^T;
        sleep rand 5;
    }
    # $dbh->disconnect; # here
    warn "Exit flag is set ... restarting\n";
    exec 'j.pl';
}
Sinan Ünür
Seemed the simplest solution and it works.
ankimal
+2  A: 

You could try registering an atexit function to close the DBI handle at the point where it is opened, and then use fork & exec to restart the script rather then just exec. The parent would then call exit, invoking the atexit callback to close the DBI handle. The child could re-exec itself normally.

Edit: After thinking for a couple more minutes, I believe you could skip the atexit entirely because the handle would be closed automatically upon the parent's exit. Unless, of course, you need to do a more complex operation upon closing the DB handle than a simple filehandle close.

my $pid = fork();
if (not defined $pid) {
    #Could not fork, so handle the error somehow
} elsif ($pid == 0) {
    #Child re-execs itself
    exec '/home/foo/bin/myscript.pl';
} else {
    #Parent exits
    exit(0);
}
Adam
I guess the only issue with that is that an exit in a thread is only a Thread->exit() and does not necessarily exit all threads.
ankimal
That should only be true if you've overridden the default behavior. Reference: http://perldoc.perl.org/threads.html#EXITING-A-THREAD
Adam
+2  A: 

If you expect a lot of connections, you probably want DBI::Gofer to act as a DBI proxy for you. You create as many connections in as many scripts as you like, and DBI::Gofer shares them when it can.

brian d foy
No, I dont expect a lot of connections. Just expect a lot of restarts!
ankimal
In that case, your problem is closing connections, not opening them.
brian d foy