在 Perl 中,跨平台告警信号处理或执行超时最可靠的方法是什么?



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

MySQL的GET_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不是用d_sigaction编译的,而Sys::SigAction也需要,所以我没有走多远。我尝试实现Perl标准的alarm/$SIG{ALRM}模式,但是在等待Postgres锁时无法发送信号。

这让我想到了这里的问题:在 Perl 中超时执行的最佳跨平台模式是什么?理想情况下,它具有一个简单的界面,适用于 *nix 和 Windows,并有效地处理数据库查询的中断。

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

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

我特别高兴意识到我可以做#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]
}

最新更新