I have a function that does quite a bit of intensive processing. Once in awhile I need to be able to stop it (i.e. for DB maintenance) even if it's mid-run. I'd like to send it a SIGINT so it can wind up what it's doing gracefully. What I found was as soon as I introduce a normal perl interrupt handler, the function stops responding to interrupts properly.
Working
CREATE OR REPLACE FUNCTION run_for_awhile() RETURNS void
AS $_X$
spi_exec_query('select pg_sleep(30)');
$_X$
LANGUAGE plperlu;
If I do select run_for_awhile()
in psql, I can type Ctrl+C
and it cancels. In the actual app I'd be doing select pg_cancel_backend(PID)
but my tests have the same outcome via that method.
If I modify the function to capture SIGINT and/or SIGTERM, the signal gets sent, but the function doesn't notice it until after 30 seconds (when pg_sleep finishes). So the handler does run eventually but not "immediately".
i.e.
CREATE OR REPLACE FUNCTION run_for_awhile() RETURNS void
AS $_X$
$SIG{INT} = sub { die "Caught a sigint $!" };
spi_exec_query('select pg_sleep(30)');
$_X$
LANGUAGE plperlu;
What you'll need to do here is have your Perl interrupt handler invoke the same C code as the CHECK_FOR_INTERRUPTS()
macro does, that is:
if (InterruptPending)
ProcessInterrupts();
(If you're on windows you need a bit more, see src/include/miscadmin.h
).
Unfortunately there's no wrapper function you can call trivially, and plperl only seems to invoke CHECK_FOR_INTERRUPTS
in plperl_spi_prepare
.
So one option you have seems to be to set a global in your Perl interrupt handler that makes your app's main loop do a SELECT 1 when it sees the variable is set. Bit ugly, but it should work.
Otherwise you can possibly use Perl's Inline::C
or similar to invoke ProcessInterrupts
.
It'd be nice if plperl exposed a Perl wrapper for CHECK_FOR_INTERRUPTS()
. Consider submitting a patch.