tags:

views:

88

answers:

2

Can someone advise on why I get errors opening the file in the code below. The errors start about half way through the 9th iteration of 25 threads, and are "Too many open files" errors. The error only happens when running in threads, and only when the DBI connect/disconnect are used. This shouldn't affect the open file count at all should it? I'm fairly new to Perl so not sure if I've done something weird. This is on Perl 5.8.8. on Solaris 10.

use threads ();
use DBI;
use DBD::Oracle;

my $thrds=25;
my $iter=10;
my @threads;

for (my $j=0; $j<$iter; $j++) {
    &start($j);
}

sub start {
    my $k=$_[0];
    for (my $i=0; $i<$thrds; $i++) {
        $threads[$i] = threads->new(\&RunThread,$k, $i);
    }
    for (my $i=0; $i<$thrds; $i++) { $threads[$i]->join; }
}

sub RunThread {
    my $dbh = DBI->connect("dbi:Oracle:lnrmsd9.world", "rms_reader", "rms_reader") or die "failed connect";
    my ($x, $y)=@_;
    open (my $fh, ">/tmp/da") or die "failed $! at iter $x thread $y";
    close ($fh);
    $dbh->disconnect;
}
+4  A: 

You need to use:

use warnings;
use strict;

These would tell you that you're using global variables $i and $j in the subroutine. Since you've got multiple threads accessing the variables, all hell breaks loose. Also, they're all sharing a single FILE, too - another source of trouble. And did you realize you had both a scalar '$threads' and an array '@threads'?

With threads, global variables are ... well, if not exactly the enemy, extremely problematic.

Avoid the FILE handle form of open; use my much more liberally.

And you don't need to say 'use DBD::Oracle;' ever. You might sometimes need to use the variant:

use DBD::Oracle qw( :ora_types );

to gain access to Oracle-specific data types.


Untested revision:

use strict;
use warnings;
use threads ();
use DBI;
use DBD::Oracle;

my $threads=25;
my $iter=10;

for ($j = 0; $j < $iter; $j++) {
    &start($j);
}

sub start {
    my($j) = @_;
    my(@threads);
    for (my $i = 0; $i < $threads; $i++) {
        $threads[$i] = threads->new(\&RunThread,$j, $i);
    }
    for ($i=0; $i < $threads; $i++) { $threads[$i]->join; }
}

sub RunThread {
    my $dbh = DBI->connect("dbi:Oracle:ora", "user", "pass") or die "failed connect";
    my($j, $i) = @_;
    open(my $fh, ">/tmp/da") or die "failed $! at iter $j thread $i";
    close $fh;
    $dbh->disconnect;
}

One thing I didn't understand - why shouldn't I use use DBD::Oracle;?

If you look at the 'perldoc DBD::Oracle', you will see the Synopsis:

use DBI;

$dbh = DBI->connect("dbi:Oracle:$dbname", $user, $passwd);

So, the primary documentation for the DBD::Oracle module shows that you do not use it directly.

There is no harm done in using it; there is no need to use it. The DBI module automatically loads the driver implied by the connection string in the call to DBI->connect(). By writing use DBD::Oracle;, you save the DBI from having to actually do the loading (it is already done). I suppose you also get Perl to verify that the module is available to be loaded with the use clause.

Jonathan Leffler
+1. Perl is one of the easiest language with which to shoot yourself in the foot: There are so many ways to shoot yourself in the foot that you post a query to comp.lang.perl.misc to determine the optimal approach. After sifting through 500 replies (which you accomplish with a short Perl script), you set to the task of simply and elegantly shooting yourself in the foot, until you discover that, while it works fine in most cases, Windows, VMS, and various flavors of Linux all let you shoot you in the foot sooner than your Perl script could. ;-)
DCookie
Perl also gives great diagnostics for fixing the foot shootings -- if the programmer chooses to make use of them.
Ether
No argument here!
DCookie
OK thanks, it was a bit messy! My excuse is that it's a quick and dirty to recreate a problem in a bigger script. Anyway, I tidied up (and have changed the code in the original post), but unfortunately I *still* get the "Too many open files" error after 9 iterations. So problem not solved yet. One thing I didn't understand - why shouldn't I use use DBD::Oracle; ?
Joe Watkins
@Joe Watkins: See the end of the answer for the 'why not use DBD::Oracle' issue. On the leaking file descriptors: beginning to look buggy. One check omitted: do the closes succeed? If not, that's where you're leaking. Otherwise, it looks to me as though the leaking must be in DBI and DBD::Oracle - the `$dbh->disconnect` appears not to be releasing the file descriptor. One other item to check - is the DBD::Oracle compiled with thread-safe OCI library and knowing that threads are going to be used. If you have a descriptor leak, report to the support mailing list `[email protected]`.
Jonathan Leffler
@Joe Watkins: see also [DBI web site](http://dbi.perl.org/). When reporting problems, you will need to report the DBI and DBD::Oracle versions, as well as the Oracle version (client software - OCI). And, if you are using archaic code, expect the first response to be 'upgrade to the current version'.
Jonathan Leffler
Excellent, thanks. I was led through this in more detail over at perlmonks (http://www.perlmonks.org/?node_id=846083) and it does appear that there's a bug, most likely on the Oracle side. I have limited ability to make changes to installed versions, so will probably need to work around, by not having so many connects. Cheers
Joe Watkins
+1  A: 

Try

my $FILE;
open ($FILE, ">/tmp/da") or die "failed $! at iter $j thread $i";
close ($FILE);

This is in the best practice.

J-16 SDiZ
Thanks, tried that, but no change
Joe Watkins