由 find_or_create_by_ 创建的重复记录

发布于 2024-11-10 09:50:26 字数 372 浏览 0 评论 0原文

我有一个 ActiveRecord 对象 Corporation,并且在我的项目中创建该对象实例的唯一调用如下所示:

corp = Corporation.find_or_create_by_eveid_and_user_id(self.corporation_eveid, self.account.user_id)

然而不知何故,在我的应用程序愉快地运行了几天后,出现了重复的记录 - 记录 eveid 的位置和 user_id 具有相同的值。这怎么可能?我更新这些记录的方式可能做错了什么,从而导致此问题?

我最终向表中添加了一个唯一的复合索引。这应该可以解决问题,但我不明白它是如何发生的。

这是 Rails 3.0.7。

I have an ActiveRecord object, Corporation, and the only call in my project to create instances of this object looks like:

corp = Corporation.find_or_create_by_eveid_and_user_id(self.corporation_eveid, self.account.user_id)

Yet somehow, after my application has been running happily for a couple of days, there are duplicate records -- record where the eveid and user_id have the same values. How is this possible? Is there something I could be doing wrong in the way I update these records that would cause this problem?

I ended up added a unique, composite index to the table. That should solve the problem, but I don't understand how it's occurring.

This is Rails 3.0.7.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暖风昔人 2024-11-17 09:50:26

find_or_create 不执行任何锁定,也不尝试阻止竞争条件。这只是一种方便的方法。如果竞争条件是一个问题,您将需要:

  1. 使用事务并回滚,如果您发现其他人已经在您之前写入
  2. (如果您实际上期望出现竞争条件,那就更好了),执行悲观锁定。这是您首先从表中获取排他锁的地方,然后执行写入并清除锁。在 MySQL InnoDB 表中,这是 SELECT ... FOR UPDATE 。如果您没有要锁定的参考点(即没有外键或数据库中已存在的任何内容),那么您必须坚持使用(1)。

编辑|如果您可以在模式级别添加 UNIQUE 约束,如果这是真正的完整性问题,我建议也这样做。

find_or_create does not perform any locking and makes no attempt to prevent race conditions. It's just a convenience method. If race conditions are a problem, you will need to either:

  1. Use a transaction and roll back if you find somebody else has written just before you
  2. (Better if you're actually expecting a race condition), perform pessimistic locking. This is where you SELECT from the table acquiring an exclusive lock first, then perform the write and clear the lock. In MySQL InnoDB tables, this is SELECT ... FOR UPDATE. If you have no reference point to lock on (i.e. no foreign key or anything that already exists in the database), then you'll have to stick with (1).

EDIT | If you can add a UNIQUE constraint at the schema level, I'd advise doing so too, if this is a genuine integrity concern.

浅唱ヾ落雨殇 2024-11-17 09:50:26

这在你的种子文件中吗?最好的选择是在模型中编写验证,以防止存在具有相同 eveid 和 user_id 的公司。

在我看来,您使用 find_or_create 播种了此信息,这有效。但也许在当天晚些时候或另一天,有人使用您的 GUI 使用相同的信息创建了另一个。验证可以防止这种情况发生。

我还没有测试过这段代码,但类似的东西可能对你有用。

validates :eveid, :uniqueness => { :scope => :user_id }

Is this in your seeds file? Your best bet would be to write validations in your model to prevent the existence of a Corporation with the same eveid and user_id.

It seems to me that you seeded this information using find_or_create, which worked. But then maybe later in the day or another day someone created another one with the same information using your GUI. Validations would prevent this.

I have not tested this code, but something like this may work for you.

validates :eveid, :uniqueness => { :scope => :user_id }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文