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

Hello. I have a script using DBI and Socket::IO. It connects to a one-way datasource using Socket::IO which spits out data. The job of the script is to read the data and format/insert it into a table if a certain condition is met. Pretty simple stuff. But what I am noticing is about a 45% data loss on the inserts (inserts never make it into the table). No errors. nada.

Here is how I set up my DB connection:

sub openDB { my $return; # Input argument: [string(DB Server hostname), String(Database name +to open)] print DEBUGFILE "Entering sub openDB\n" if ($DEBUG); # requires use of require "/home/reporting/nwCentral/dbServers.pl" l +ine my $mysqlHost=shift; my $mysqlDB=shift; my $host = $dbServers{"$mysqlHost"}{'host'}; my $mysqlUser = $dbServers{"$mysqlHost"}{'user'}; my $mysqlPass = $dbServers{"$mysqlHost"}{'pass'}; my $mysqlHostQry = "DBI:mysql:$mysqlDB:$host"; my $dbh=DBI->connect($mysqlHostQry, $mysqlUser, $mysqlPass) or $retu +rn=0; if ($return){ return $dbh; } else { return 0; } }

And for the Socket Connection

sub openDATASOURCE{ print DEBUGFILE "Entering sub openDATASOURCE\n" if ($DEBUG); my $socketInfo=$ipPort{$socketName}; my($remote_host, $remote_port)=split(/ /, $socketInfo); if ($usingSocket) { $DATASOURCE = new IO::Socket::INET->new(PeerAddr => $remote_host, PeerPort => $remote_port, Proto => "tcp", Type => SOCK_STREAM) or $DATASOURCE=0; if ($DATASOURCE) { $return=1; $DATASOURCE->autoflush(1); #Make the IO hot -- disable buffering print LOGFILE "$sysDate: IO: Connected to $remote_host $remote_p +ort\n"; &updateSocketStatus("$$", "1"); } else { $return=0; print LOGFILE "$sysDate: IO: Cannot open datasource: $@\n"; } } return $return; if (!($usingSocket)) { open (DATASOURCE, "<$namedPipe") or die "Couldn't open $namedPipe: + $!\n"; } }

And for how the data is inserted - read the socket, grab the data, format it and push it into an array. The array is then sent to the insert routine:

sub doSQL { print DEBUGFILE "Entering sub doSQL\n" if ($DEBUG); my (@localArray, $sqlStatement, $size)=(); (@localArray)=@_; $size=scalar (@localArray); $sqlStatement="INSERT INTO $liveTable SET @localArray"; if ($usingDB) { my $sth=$mainDBh->prepare($sqlStatement) or &exitAll("notok", "doS +QL:\nQuery= $sqlStatement\nError=" . $mainDBh->errstr); $sth->execute() or &exitAll("notok", "doSQL:\nQuery= $sqlStatement +\nError=" . $mainDBh->errstr); } print DEBUGFILE "$sqlStatement\n" if $DEBUG; }

As you can see, my DEBUG information contains the exact query sent to the DBI handler. I have identified the missing rows and performed a manual execution of the query without problems. Again, no errors were reported in any log.

I have also ensured the MySQL configuration is identicle to another server performing a similar task (different formatting). The DB is not showing any issues, only about 8 to 30 queries/second, but I am not refusing to believe it is in fact a DB configuration/performance issue (the DB files are on SAN -- which is a difference).

I have a workaround: load the data into a file and do a load data infile every 30 seconds. However this is bugging me since it SHOULD work (and I hate writing temp files). Come to think of it, maybe my error catching is problematic?

Apologies for the lengthy post, but wanted to provide detail. Thank you for taking the time to read this.

Matt

Replies are listed 'Best First'.
Re: MySQL DBI an Socket::IO dropping inserts somewhere
by almut (Canon) on Oct 17, 2009 at 10:31 UTC

    If the debug print (print DEBUGFILE "$sqlStatement\n" if $DEBUG;) shows the query as expected (as I'm understanding you), then it's unlikely that the IO::Socket part has anything to do with the problem. In other words, I'd focus on the DB side, and enable tracing to figure out what happens (or doesn't)...

    BTW, where are you setting $usingDB? (I suspect it's some global setting, but you don't show where you set it...)  In case of doubt, put the debug print within the if ($usingDB) { ... } where the actual submission to the DB is supposed to happen.

      Thanks for the link to tracing. I am reading up on it now. I will take your advice and concentrate on the DB side of things; I'll reply back with any tracing discoveries.

      Also, you are correct, $usingDB is a global set in the beginning of the script. I've seen stranger things; printing it out is a great tip.

      Cheers!
Re: MySQL DBI an Socket::IO dropping inserts somewhere
by graff (Chancellor) on Oct 17, 2009 at 14:36 UTC
    I was puzzled by your socket connection sub. First, either you aren't using strict, or else there's a (global?) declaration for the variable "$return" that you aren't showing. Second, you do a "return $return;" followed by an "if(...)" -- and of course the "if" statement can never be reached.

    That might not have anything to do with your problem, but if you add "use strict;" and clean things up, the real problem might be revealed. BTW, I notice that the "doSQL" sub refers to $usingDB, which not passed to or declared within the sub. This is generally considered poor form. It's better for the sub to be fully self-contained, not referring to any globals outside its own scope. (And anyway, if $usingDB is false, this sub shouldn't have been called in the first place.)

    As for database performance, perhaps you can figure out a way to use a consistent sql statement with placeholders, such that a given statement only needs to be prepared once per run connection (I presume there's one connection per run). Something like this:

    sub doSQL { my ( $tablename, $fldnames, $fldvals ) = @_; # change call params +: # string, array_ref, array_ref my $sqlstring = "insert into $tablename (", join( ',', @$fldnames +) . ') values (' . join( ',', ('?') x scalar @$fieldnames ) . ')'; my $sth = $dbh->prepare_cached( $sqlstring ); $sth->execute( @$fldvals ); # add error checking as desired... }
    (updated to fix a misspelled variable name)

    You'll probably want to read the section in the DBI manual about the "prepare_cached" function.

Re: MySQL DBI an Socket::IO dropping inserts somewhere
by whiteonline (Novice) on Oct 19, 2009 at 07:17 UTC
    Well, it's fixed now.

    I checked level 2 trace which showed identical output on both successful and unsuccessful inserts.
    Then I changed the insert format as suggested by graff, along with using the prepare_cached method.

    I believe the fault may have been as a result of my ugly insert syntax. Instead of using, INSERT INTO table (colums) VALUES (values), I was using, INSERT INTO table SET column1=value1, column2=value2. Not sure as to why there was a difference, but I am happy - for now - to accept it, but eventually would like to find out why no errors were created (is it DBI or the MySQL client/engine?).

    Thank you to all who have read this post and especially to those who had the insight to post/cb.

    Cheers!
    matt