Rails 独特的验证不起作用和后台作业

问题描述 投票:0回答:2

我有一个模型名为约会的应用程序。在此模型上,有一个名为

event_uid
的列和如下所示的验证:

validates :event_uid, uniqueness: true, allow_nil: true

唯一的验证仅在 Rails 应用程序上进行,而不是在数据库(postgresql)中进行。

我正在使用 Heroku 上的 sidekiq 后台作业来同步一些远程日历。我不确定发生了什么,但似乎我得到了多个具有重复

event_uid
值的记录。它们是在同一秒内创建的。

我的猜测是,工作人员身上发生了一些事情,由于某种原因,他们同时被调用,或者队列被冻结,当它回来时,它运行了相同的工作两次。我不明白为什么 Rails 让上述内容通过(也许是因为在不同线程上运行的工作人员发挥了作用?)。我添加了以下迁移:

add_index :appointments, [:event_uid], unique: true

希望这样的事不再发生。好的,现在问题是:

  • 你觉得怎么样,这样就够了吗?
  • 如果您在后台作业中使用创建/更新,则仅允许在应用程序级别存在唯一/存在验证是否危险?
  • 你猜猜是什么导致工人们在同一秒内多次执行相同的工作?
ruby-on-rails database postgresql activerecord sidekiq
2个回答
2
投票

Rails 唯一性验证长期以来一直是造成混乱的原因。

当您保留用户实例时,Rails 将通过运行 SELECT 查询来验证您的模型,以查看提供的电子邮件是否已存在任何用户记录。 假设记录被证明是有效的,Rails 将运行 INSERT 语句来保留用户。

https://thoughtbot.com/blog/the-perils-of-uniqueness-validations

这意味着,如果您有多个工作线程/线程同时选择,它们都会返回 false 并插入记录。

大多数时候,最好在数据库级别有一个索引,以避免这些竞争条件。但是,您现在还需要处理任何

ActiveRecord::RecordNotUnique
异常。

你觉得怎么样,这样就够了吗?

是的,添加索引是个好主意,但现在您还需要处理

ActiveRecord::RecordNotUnique

如果您在后台作业中使用创建/更新,则允许仅在应用程序级别存在唯一/存在验证是否危险?

这取决于应用程序,但大多数时候您也希望在数据库级别有一个索引。

你猜猜是什么导致工人在同一秒内多次执行相同的工作?

大多数后台作业库仅保证至少一项作业入队,但不保证恰好一项。您的作业应该始终是幂等(可以运行多次)。一本关于ActiveJob设计的指南值得一读,尤其是关于幂等性的部分。


1
投票

通常,验证仅在回调期间在 Rails 中进行(有时在数据库上提交记录之前),是的,如果您添加了唯一索引,这种情况不会再次发生,因为这次数据库将负责,所以即使您遇到相同的情况再次流动/发出结果可能是一个错误,表明您无法复制该索引值。

考虑到验证器的性质(通常在回调期间调用并且不是线程安全的)意味着它们可能会遇到竞争条件,这种情况发生的常见程度取决于您的应用程序,您应该始终在数据库上添加验证,如下所示好吧。

与您的工作人员相关,由于几个月前的

Sidekiq
重试流程,我遇到了同样的问题,解决方案是也在数据库端进行验证并修复以运行工作人员/作业
after_commit
回调(不确定您是否使用
Sidekiq
,但您始终可以使用
after_commit
回调,我在对特定对象进行某些操作后使用我的工作)。

希望以上内容对您有所帮助! 👍

© www.soinside.com 2019 - 2024. All rights reserved.