tags:

views:

1675

answers:

2

Hi,
is there an easy way to timeout an SQL statement so that it will fail instead of waiting (e.g. deliver an empty result set or an error message or whatever else) so I can let a job's ressource reservation fail and give another one a chance? I'm looking for some DBI option I've overlooked so far; sending SIGALRMs to myself to commit suicide is rather not what I have in mind (although I might have to resort to that if I had to).

The code snipped is pseudo-ized and shortened to the extreme, but I hope you'll catch the drift.

my $sql = "SELECT one, two, three FROM sometable WHERE this = ? AND that = ?";
my $sth = $self->make_handle( $sql );
eval {
    foreach my $this ( sort keys %needed_ressources ) {
        # vvv This is where the idle time is spent vvv
        $sth->execute( $this, $that ) or die( "DB connection gone?!" );
        # ^^^ This is where the idle time is spent ^^^
        my ( $one, $two, $three ) = $sth->fetchrow_array();
        unless( $one ) { # undefined record set == not found
            $self->{DB_HANDLE}->rollback();
            die( "$this not defined for $that!" );
        }
    }
    # If we haven't died so far, we can move on
    foreach... #similar loop here doing the actual update statement
    $self->{DB_HANDLE}->commit();
};
return( 1 ) unless $@;
return( undef );

Here are the gory details for the interested:

In an application that does massively parallel numbercrunching there is a ressource locking mechanism implemented that uses an oracle table. Each job needs to lock a number of ressources for reading and/or a number of ressources to write to, and can only start if all the locks have been acquired successfully. Instead of patiently waiting for ressources to be freed, jobs should just fail and be re-run later by their master (this keeps the number of open transactions low while boosting performance by having more jobs around that actually crunch).

Of course, before actually updating the table, each row is being reserved using a "SELECT ... FOR UPDATE" statement so Oracle uses row level locking and concurrent transactions can happen on the table. In order to further reduce possible race conditions and deadlocks all jobs first select their ressource rows and then row lock them using the same ordering before performing the update.

As of the current implementation this works fine in most of the cases. But, because the "Select for update" blocks until Oracle actually grants the row lock, it may still happen that a job is idle waiting for its ressources, and I'm after those to better utilize the available CPU power. It's OK to wait a second or two, but not ten or more just for the locking. For unlocking later, waiting is required of course, so setting the whole DB connection to only accept immediate results will not work.

I'm always grateful for RTFM answers as long as they point to the location in the M that I should TF have R ;-))

Thanks a lot in advance,
Olfan

+2  A: 

Actually, SIGALRM may not be so bad after all. There are some options listed here.

Chris Simmons
Of course you're right. But I really want to keep the worker processes out of implementing their own signal handling especially when it differs from the one their father process has implemented.
Olfan
+7  A: 

I think you want the NOWAIT parameter on the FOR UPDATE clause. If the record cannot be locked, the select will fail ("ORA-00054: resource busy and acquire with NOWAIT specified") and you can handle the exception however you need to. Check out the SQL Reference manual. It's for 11g, but the syntax hasn't changed for several versions now.

Another option is to give a time to wait: "FOR UPDATE WAIT 3" to wait 3 seconds for the lock to be acquired rather than failing immediately.

DCookie
This is just great, thanks. Why tinker around with Perl and DBI when the SQL itself already brings right the option I want? ;-) It's already implemented and will go live in the next possible change window. Thanks again.
Olfan
Perl/DBI has a greater geek quotient ;-)
DCookie
It's live, with the waiting time being a configurable option. Although much of the time this change saves is now just being spent waiting for mass storage (my next target;-), overall CPU utilization (==raw throughput) raised by almost 2% which absolutely makes it worthwhile.
Olfan
xlnt! Thnx for the followup...
DCookie