ruby-on-rails – find_or_create竞争条件

我正在尝试使用ActiveRecord的find_or_create_by_ *列*,但是我从Postgres得到错误让我知道它偶尔找不到模型,并试图插入一个.我保持这个表的独特性非常重要,所以我添加一个:unique =>这是其迁移的真正属性,因此Postgres会知道我对此很认真.

并且,失败:

ActiveRecord :: StatementInvalid:PGError:ERROR:重复键值违反唯一约束“index_marketo_leads_on_person_id”DETAIL:键(person_id)=(9968932)已存在. :INSERT INTO“marketo_leads”(“mkt_person_id”,“synced_at”,“person_updated_at”,“person_id”)VALUES(NULL,NULL,’2011-05-06 12:57:02.447018′,9968932)返回“id”

我有这样的模型:

class User < AR::Base
  has_one :marketo_lead

  before_save :update_marketo_lead

  def update_marketo_lead
    if marketo_lead
      if (User.marketo_columns & self.changes.keys).any?  
        marketo_lead.touch(:person_updated_at) 
      end
    elsif self.id
      marketo_lead = MarketoLead.find_or_create_by_person_id(:person_updated_at => Time.Now,:person_id => self.id) 
    end
  end
end

class MarketoLead
  belongs_to :user,:foreign_key => 'person_id'
end

第二个模型用于将我们的用户帐户链接到Marketo电子邮件服务器,并保留上次更改用户的某些字段的记录,以便我们可以在批量后台任务中推送更改的记录.

我无法想到这个回调的任何原因,update_marketo_lead失败,除了某些我无法想象的竞争条件.

(请忽略’用户’与’人’共享主键的可怕性)
(使用Rails 2.3.11,Postgres 9.0.3)

解决方法

很可能当执行find_or_create时,找不到匹配的person_id,因此使用了create logic,但是它可能在find_or_create和实际user.save之间,另一个请求设法完成保存事务,此时你的数据库约束导致了这个异常.

我建议的是捕获StatementInvalid异常并重试保存(最多有限次数

begin
   user.save!
rescue ActiveRecord::StatementInvalid => error
  @save_retry_count =  (@save_retry_count || 5)
  retry if( (@save_retry_count -= 1) > 0 )
  raise error
end

请注意,这应该在您尝试保存用户的任何位置执行.所有回调和验证都在保存中发生!交易

附:我假设您的rails版本支持事务:)在Rails 3中,它不需要包装保存!在事务中因为它已经在内部使用了一个

相关文章

validates:conclusion,:presence=>true,:inclusion=>{...
一、redis集群搭建redis3.0以前,提供了Sentinel工具来监控各...
分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣...
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 mo...
一编程与编程语言 什么是编程语言? 能够被计算机所识别的表...
Ruby类和对象Ruby是一种完美的面向对象编程语言。面向对象编...