首页 > 解决方案 > Perl NFS 锁的奇怪行为

问题描述

我在 Perl v5.16 中遇到来自 File::NFSLock 的奇怪行为。我使用过时的锁定超时选项为 5 分钟。假设我有三个进程。其中一个在释放锁之前花费了 5 分钟以上,并且进程 2 获得了锁。然而,即使进程 2 的锁定时间不到 5 分钟,第三个进程即将到来并删除锁定文件,导致第二个进程在删除自身持有的 NFSLock 时失败。

我的理论说进程 3 错误地读取了锁的最后修改时间,因为它是由进程 1 而不是进程 2 写入的。我正在对挂载在 NFS 上的分区写入 nfs 锁。

有人对 perl NFSLock 有想法或面临类似问题吗?请参考以下快照

my $lock = new File::NFSLock {file      => $file,
                              lock_type => LOCK_EX,
                              blocking_timeout   => 50,     # 50 sec
                              stale_lock_timeout => 5 * 60};# 5 min

$DB::single = 1;
if ($lock) {
    $lock->unlock()
}

如果我在进程 1 的调试器点阻塞超过 5 分钟,我正在观察这种行为

标签: perllockingnfstransient-failure

解决方案


通过查看
https://metacpan.org/pod/File::NFSLock
上的代码, 我看到 Lock 仅由系统中的物理文件实现。
我几乎在每个项目中都使用相同的进程锁定逻辑。

使用 Process Lock 至关重要的是不要设置stale_lock_timeout得太紧。
或者它会发生“竞争条件”,因为它也在代码注释中提到。

正如您所提到的,这 3 个进程开始竞争同一个锁,因为作业需要> 5 分钟并且您将其设置tale_lock_timeout为 5 分钟。
如果您有像crond服务这样的固定时间提供者,这将每 5 分钟启动一个进程。每个进程都将 Lock 视为已过时,因为尽管该进程需要超过5 分钟,但已经过了 5 分钟

描述一个可能的场景:
一些 DB 作业需要 4 分钟才能完成,但在拥塞的系统上可能需要 7 分钟或更长时间。
现在,如果crond服务每 5 分钟启动一个进程,
在 0 分钟时,第一个进程process1将发现作业是新的,并设置锁定并启动作业,这将需要 7 分钟。
现在在 5 分钟时,crond服务将启动process2,它会找到 Lock ,process1但决定它已经过时,因为自 Lock 创建以来已经 5 分钟,它将被视为stale。所以process2释放锁并为自己重新获取它。
稍后7 分钟 process1已经完成了 Job 并且没有检查它是否仍然是他的 Lock 它释放 Lockprocess2并完成。
现在在 10 分钟 process3启动并没有找到任何锁,因为锁process2已经被释放process1并设置了自己的锁。
这种情况实际上是有问题的,因为它会导致流程积累和工作量积累以及不可预测的结果。

解决此问题的建议是:

  1. 设置stale_lock_timeout为远大于工作所需的量(如 10 分钟或 15 分钟)。但stale_lock_timeout比执行时间安排要大。
  2. 将执行计划设置得更宽敞,以便为每个进程提供足够的时间来完成其任务(每 10 分钟或每 15 分钟)
  3. 考虑将process1,process2和 的作业集成process3到一个仅process_master在前一次完成后启动每个进程的作业中。

推荐阅读