我如何处理中断(即 SIGTERM 或 SIGINT)

How do I handle interrupts (i.e. SIGTERM or SIGINT)

我有一个函数可以进行相当多的密集处理。偶尔我需要能够停止它(即为了数据库维护),即使它是 mid-运行。我想向它发送一个 SIGINT,这样它就可以优雅地结束它正在做的事情。我发现,只要我引入一个普通的 perl 中断处理程序,该函数就会停止正确响应中断。

工作

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

如果我在 psql 中执行 select run_for_awhile(),我可以输入 Ctrl+C 并且它会取消。在实际的应用程序中,我会做 select pg_cancel_backend(PID) 但我的测试通过该方法有相同的结果。

如果我修改函数以捕获 SIGINT and/or SIGTERM,信号被发送,但函数直到 30 秒后(pg_sleep 完成时)才注意到它。所以处理程序最终会 运行 但不会 "immediately".

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;

您在这里需要做的是让您的 Perl 中断处理程序调用与 CHECK_FOR_INTERRUPTS() 宏相同的 C 代码,即:

    if (InterruptPending)
        ProcessInterrupts();

(如果您使用 windows,您还需要更多信息,请参阅 src/include/miscadmin.h)。

不幸的是,没有可以简单调用的包装函数,而且 plperl 似乎只在 plperl_spi_prepare.

中调用 CHECK_FOR_INTERRUPTS

所以您的一个选择似乎是在您的 Perl 中断处理程序中设置一个全局变量,使您的应用程序的主循环在看到变量已设置时执行 SELECT 1。有点丑,但应该可以。

否则你可以使用 Perl 的 Inline::C 或类似的方法来调用 ProcessInterrupts.

如果 plperl 为 CHECK_FOR_INTERRUPTS() 公开一个 Perl 包装器就好了。考虑提交补丁。

我还没有完全弄清楚这个问题,但我找到了解决方法。 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;