首页 > 解决方案 > Perl 中跨平台警报信号处理或执行超时最可靠的方法是什么?

问题描述

我使用Postgres 咨询锁MySQL GET_LOCK()向Sqitch添加了咨询锁定。此功能可防止一次将多个 Sqitch 实例部署到数据库。这很好用,但我也想添加一个锁定超时,这样就永远不会发现 CI/CD 进程因为出现问题而挂起数小时或数天。

MySQLGET_LOCK()支持超时参数,但 Postgres 咨询锁不支持。因为我认为其他数据库引擎也可能没有超时,所以我认为最好在 Perl 中实现超时。按照 DBI 手册,我使用Sys::SigAction来设置和处理超时

# Try waiting for the lock.
require App::Sqitch::SigAction;
return $self->_locked(1) unless App::Sqitch::SigAction::timeout_call($wait, sub {
    $self->wait_lock
});

我还添加了测试以确认它适用于 MySQL 和 Postgres。到目前为止,一切都很好。

唉, Sys::SigAction在 Windows 上不起作用。我试了一下并在 Windows 上对其进行了测试,但由于 Windows Perl 不是用Sys::SigAction 也需要d_sigaction的 编译的,所以我没有走多远。我尝试实现Perl-standard / pattern,但在等待 Postgres 锁时无法发送信号。alarm$SIG{ALRM}

这让我想到了我的问题:在 Perl 中超时某些执行的最佳跨平台模式是什么?理想情况下,它有一个直接的界面,可以在 *nix 和 Windows 上工作,并且可以有效地处理数据库查询的中断。

标签: postgresqlperltimeoutsignalsalarm

解决方案


在此处和其他地方的讨论之后,我最终放弃了 Sys::SigAction,而是切换到:

  1. 让数据库处理超时,就像 MySQLget_lock()一样
  2. 添加一个简单的轮询接口,带有指数退避和超时,引擎可以使用该接口轮询锁定而不是等待(类似于Retry::Backoff
  3. 切换 Postgres 实现以使用 DBD::Pg 中的异步查询支持来发送锁定请求,并使用退避/超时接口检查它是否已返回,如果超时则取消查询

我特别高兴地意识到我可以做到#3,因为我最初使用超时/退避接口来轮询pg_try_advisory_lock( key ),这感觉很重。最好异步调用pg_advisory_lock ( key )和轮询它的响应。它看起来像这样

sub wait_lock {
    my $self = shift;
    # Asyncronouslly request a lock with an indefinite wait.
    my $dbh = $self->dbh;
    $dbh->do(
        'SELECT pg_advisory_lock(75474063)',
        { pg_async => DBD::Pg::PG_ASYNC() },
    );

    # Use _timeout to periodically check for the result.
    return 1 if $self->_timeout(sub { $dbh->pg_ready && $dbh->pg_result });

    # Timed out, cancel the query and return false.
    $dbh->pg_cancel;
    return 0;
}

当然MySQL 的实现更简单,因为get_lock()所有的工作都完成了:

sub wait_lock {
    my $self = shift;
    $self->dbh->selectcol_arrayref(
        q{SELECT get_lock('sqitch working', ?)},
        undef, $self->lock_timeout
    )->[0]
}

推荐阅读