Safely timeout DBI with DBIx::Timeout

samtregar on 2006-09-17T19:28:29

Recently I needed to find a way to timeout a DBI request. I found the state of the art less than satisfying, involving unsafe signals and a chance of memory corruption deep in Perl's guts:

http://search.cpan.org/user/timb/DBI/DBI.pm#Signal_Handling_and_Canceling_Operations

This led me to create DBIx::Timeout which instead of using unsafe signals:

  - Forks a child process which sleeps for $timeout seconds.

  - Runs your long-running query in the parent process.

  - If the parent process finishes first it kills the child and
    returns.

  - If the child process wakes up it kills the parent's DB thread and
    exits with a code so the parent knows it was timed out.

Tim Bunce suggested a possible optimization - fork just one child process and have it watch any number of slow queries simultaneously. It would accept assignments via a pipe interface. Seems like a good idea, although it's likely overkill for my usage. The queries I need to timeout are very likely to be long-running, and when they're not don't need to be very fast. The overhead of forking a process that exits almost immediately won't cause any problems, I'm betting.

So, please give it a try! And if you're not a MySQL user, please port it to your DB and send me a patch. It should be an easy job - all you have to do is implement a call to kill another process in the DB (MySQL does it with "KILL $thread_id"). (UPDATE: actually, it's a little more work - you also need to write new tests. The tests I wrote for MySQL use GET_LOCK() to test timeouts - you'll need to do something analgous for your DB.)

-sam

PS: I should note that the mechanism this module uses was suggested by my co-worker Perrin Harkins. I'll add that to the module's POD for the next release.