【问题标题】:What's the most reliable method for cross-platform alarm signal handling or execution timeouts in Perl?Perl 中跨平台警报信号处理或执行超时最可靠的方法是什么?
【发布时间】:2021-10-15 13:20:11
【问题描述】:

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

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

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

我还添加了tests 以确认它适用于 MySQL 和 Postgres。到目前为止一切顺利。

唉,Sys::SigAction does not work on Windows。我采取了刺和testing it on Windows,但由于Windows Perl 不是用d_sigaction 编译的,Sys::SigAction also requires,我没有走多远。我尝试实现Perl-standard alarm/$SIG{ALRM} pattern,但在等待 Postgres 锁时无法发送信号。

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

【问题讨论】:

  • Widnows 没有信号。 (Ctrl-C 和 Ctrl-Break 确实有类似的东西,但仅此而已。)所以所问的问题没有答案。没有通用的跨平台通用超时机制。有针对特定领域的解决方案。例如,如果你想做 IO,你可以使用异步 IO 库之一。如果要杀死整个进程,可以在 unix 中使用信号,在 Windows 中使用休眠线程。等等 MySQL 库可能是根据 unix 系统调用(openwrite 等并希望您使用信号)而不是考虑 Windows 编写的。
  • (这不是一个“unix 比 Windows 更好的东西,这是关于 C 库(以及 Perl 代码)几乎总是被编写为 unix 程序而不是 Windows 程序,即使是那些打算在其上运行的程序Windows。操作系统有根本不同的处理方法,C 标准库非常以 unix 为中心。)
  • 异步 io 库是什么样的?它唯一需要超时的是调用like$dbh->do('SELECT pg_advisory_lock(75474063)');。但是,由于它获取了数据库会话锁,因此它不能是单独的进程或线程。
  • 这里没用,因为你不做 IO。虽然可能有异步数据库库?
  • 在 postgres 中有 pg_try_advisory_lock ( key ) 返回 false 而不是阻塞。

标签: postgresql perl timeout signals alarm


【解决方案1】:

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

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

我特别高兴地意识到我可以做到#3,因为我最初使用超时/退避接口来轮询pg_try_advisory_lock( key ),感觉很沉重。最好异步调用pg_advisory_lock ( key ) 并轮询其响应。好像是this:

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 implementation 更简单,因为get_lock() 完成了所有工作:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-28
    • 2010-09-28
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    相关资源
    最近更新 更多