Nie*_* B. 14 sql postgresql activerecord locking ruby-on-rails
我有一些Rails ActiveRecord代码,如下所示:
new_account_number = Model.maximum(:account_number)
# Some processing that usually involves incrementing
# the new account number by one.
Model.create(foo: 12, bar: 34, account_number: new_account_number)
这段代码本身很好用,但我有一些由DelayedJob工作人员处理的后台作业.有两个工作者,如果他们都开始处理一批处理此代码的作业,他们最终会创建Model具有相同account_number的新记录,因为找到最大值和创建具有更高帐户的新记录之间的延迟数.
现在,我已经通过在数据库级别向模型表添加唯一性约束来解决它,然后通过在该约束触发异常的情况下重新选择最大值来重试.
然而,感觉就像一个黑客.
在数据库级别向account_number列添加自动递增不是一种选择,因为account_number分配不仅仅需要递增.
理想情况下,我想锁定有问题的表进行读取,因此在完成之前,没有其他人可以对表执行最大选择查询.但是,我不知道该怎么做.我正在使用Postgresql.
Max*_*ace 14
基于ActiveRecord :: Locking文档,看起来Rails没有为表级锁提供内置API.
但您仍然可以使用原始SQL执行此操作.对于Postgres来说,这看起来像
ActiveRecord::Base.transaction do
  ActiveRecord::Base.connection.execute('LOCK table_name IN ACCESS EXCLUSIVE MODE')
  ...
end
锁必须在事务中获取,并在事务结束后自动释放.
请注意,您在此处使用的SQL将根据您的数据库而有所不同.
关于如何锁定整个表已有答案,但我相信你应该尽量避免这种情况.相反,我相信你应该给一些咨询锁.它确保同时在两台机器上执行相同的代码块,同时仍然保持表对其他业务开放.
它仍然使用数据库,但它不会锁定您的表.
您可以像这样使用名为"with_advisory_lock"的gem:
Model.with_advisory_lock("ADVISORY_LOCK_NAME") do
  # Your code
end
https://github.com/ClosureTree/with_advisory_lock
它不适用于SQLite.