【问题标题】:Can I update an Active Record in an after_commit(on: :create) callback?我可以在 after_commit(on: :create) 回调中更新 Active Record 吗?
【发布时间】:2014-02-23 23:11:45
【问题描述】:

创建记录后,我会发送一封电子邮件,这会在after_commit 回调中进行。我想将电子邮件的Message-Id 标头保存为记录中的属性以供以后使用。我将其实现为:

after_commit on: :create do
  if email = Mailer.email(self).deliver
    # `self.message_id = email.message_id` has no effect, so I'm calling update()
    self.update message_id: email.message_id
  end
end

令人惊讶的是(对我来说),这会导致无限的电子邮件发送循环(对不起,Mailgun);似乎在更新时调用了回调即使指定了on: :create

我没有看到这种方法有什么问题吗?我还能如何将此值附加到此记录?

我唯一的想法是尝试gating the callback on previous_changes,但无论如何我想了解为什么这不能按原样工作。

【问题讨论】:

  • 我想到比previous_changes 更简单的方法是仅在message_id 尚未设置时才发送电子邮件。这可能会起作用,但我仍然感到困惑,为什么它不能按原样工作。

标签: ruby-on-rails activerecord callback


【解决方案1】:

“我没有看到这种方法有什么问题吗?”

我还期望after_commit 回调中的update 仅触发on: update 回调。但是我在 Rails 源码中找到了this

def committed!(should_run_callbacks = true) #:nodoc:
  _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
ensure
  force_clear_transaction_record_state
end

只有在运行所有 after_commit 回调之后,Rails 才会清除事务的 new_record 状态。

“我还能如何将此值附加到此记录?”

想法1:

self.update_columns message_id: email.message_id

这不会创建事务,因此不会触发另一个 after_commit 回调。我认为在事务外的记录中发送电子邮件和存储 id 是合适的。毕竟,如果出现故障,您将无法回滚带有消息 ID 的更新记录回滚发送电子邮件。它本质上不是原子的。

想法2:

after_commit 回调队列中,ActiveJob 作业发送电子邮件并使用消息 ID 更新记录。使用像 SuckerPunch 或 DelayedJob 这样的真实后端,作业将与记录的“Global ID”一起排队。 Job 的perform 方法获取一个新加载的记录,其实例变量中没有存储任何事务状态。

【讨论】:

    【解决方案2】:

    这可能有效

    after_commit :email_send, :if => lambda{ new_record? }
    

    after_commit :email_send, :on => :create
    

    after_create :email_send
    
    
    def email_send
        if email = Mailer.email(self).deliver
            # `self.message_id = email.message_id` has no effect, so I'm calling update()
            self.update message_id: email.message_id
        end
    end
    

    【讨论】:

      【解决方案3】:

      检查message_id 的先前存在似乎已经解决了我的特殊情况下的问题:

      after_commit on: :create do
        unless self.message_id
          if email = Mailer.email(self).deliver
            self.update message_id: email.message_id
          end
        end
      end
      

      但我想可能会出现我无法摆脱这种情况的情况,因此最好理解为什么在更新时会调用 on: :create 回调。

      【讨论】:

        猜你喜欢
        • 2012-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多