Как мне обрабатывать прерывания (например, SIGTERM или SIGINT)

У меня есть функция, которая выполняет довольно интенсивную обработку. Время от времени я должен быть в состоянии остановить это (то есть для обслуживания БД), даже если это в середине. Я хотел бы отправить SIGINT, чтобы он мог завершить то, что он делает изящно. Я обнаружил, что, как только я представлю обычный обработчик прерываний Perl, функция перестает правильно реагировать на прерывания.

За работой

CREATE OR REPLACE FUNCTION run_for_awhile() RETURNS void
    AS $_X$
        spi_exec_query('select pg_sleep(30)');
$_X$
LANGUAGE plperlu;

Если я сделаю select run_for_awhile() в psql я могу напечатать Ctrl+C и это отменяет. В реальном приложении я буду делать select pg_cancel_backend(PID) но мои тесты имеют тот же результат с помощью этого метода.

Если я изменю функцию для захвата SIGINT и / или SIGTERM, сигнал будет отправлен, но функция не заметит его до истечения 30 секунд (когда pg_sleep завершит работу). Таким образом, обработчик запускается в конце концов, но не сразу.

т.е.

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;

2 ответа

Решение

Здесь вам нужно, чтобы ваш обработчик прерываний Perl вызывал тот же код C, что и CHECK_FOR_INTERRUPTS() макрос делает, то есть:

    if (InterruptPending)
        ProcessInterrupts();

(Если вы находитесь на окнах, вам нужно немного больше, см. src/include/miscadmin.h).

К сожалению, нет никакой функции-обертки, которую вы можете вызвать тривиально, и plperl только вызывает CHECK_FOR_INTERRUPTS в plperl_spi_prepare,

Таким образом, у вас есть один вариант - установить глобальную переменную в вашем обработчике прерываний Perl, которая заставляет основной цикл вашего приложения делать SELECT 1, когда он видит, что переменная установлена. Немного некрасиво, но это должно работать.

В противном случае вы можете использовать Perl Inline::C или аналогичный, чтобы вызвать ProcessInterrupts,

Было бы неплохо, если бы plperl выставил Perl-оболочку для CHECK_FOR_INTERRUPTS(), Рассмотрите возможность отправки патча.

Я не смог полностью разобраться в этом, но я нашел способ обойти это. SIGINT и SIGTERM недоступны, поэтому не используйте их. Но я могу успешно отправить еще одно прерывание, например, SIGPROF. После того, как это отправлено, следите за SIGINT, и все идет как ожидалось.

CREATE OR REPLACE FUNCTION run_for_awhile() RETURNS void
    AS $_X$
        $SIG{PROF} = sub { die "Caught a sigprof $!" };

        spi_exec_query('select pg_sleep(30)');
$_X$
LANGUAGE plperlu;
Другие вопросы по тегам