postgresql - 使用 postgres 数据库锁定并发 cron
问题描述
我想要实现的是让同一应用程序的多个实例同时运行,但只有其中一个实例运行 cron,方法是将其锁定在 Postgres 数据库中。
到目前为止,我的解决方案是:
- 在所有实例上运行 cron。
- 在表cron_lock中插入具有cron 唯一标识符的行。
- 如果我在运行插入查询时出错,很可能是因为该行已经存在(cron 标识符是表的主键)。如果是这样,我什么都不做,然后我退出。
- 如果我在运行插入查询时没有出错,那么应用程序实例将运行 cron 进程。
- 在我的过程结束时,我删除了具有唯一标识符的行。
该解决方案有效,但我不确定 Postgres 是否存在另一种锁定机制,特别是不会让我执行产生错误的查询的机制。
解决方案
感谢@Belayer,我找到了一种使用咨询锁的好方法。
这是我的解决方案:
我的每个 cron 都有一个关联且唯一的 ID(整数格式)。所有的 crons 都在所有不同的服务器上启动。但在运行 cron 的主要功能之前,我尝试使用数据库中的唯一 ID 获取咨询锁。如果 cron 可以得到锁,那么它将运行 main 函数并释放锁,否则,它就停止。
如果您想用您选择的语言实现它,这里有一些伪代码:
enum Cron {
Echo = 1,
Test = 2
}
function uniqueCron(id, mainFunction) {
result = POSTGRES ('SELECT pg_try_advisory_lock($id) AS "should_run"')
if(result == FALSE){ return }
mainFunction()
POSTGRES ('SELECT pg_advisory_unlock($id)')
}
cron(* * * * *) do {
uniqueCron(Cron.Echo, (echo "Unique cron"))
}
cron(*/5 * * * *) do {
uniqueCron(Cron.Test, (echo "Test"))
}
多次运行这个过程,或者在许多不同的服务器上,都使用同一个数据库,将导致一次只执行一个 mainFunction,因为所有的 crons 都是同时启动的(不同服务器上的相同时间/时区)。如果一个服务器尝试获取锁而另一台服务器已经释放它,那么主函数太短而无法执行可能会导致问题。在这种情况下,请稍等片刻再释放锁。