【问题标题】:dependent: :destroy - can I delay this with Delayed Job?dependent: :destroy - 我可以用延迟工作来延迟这个吗?
【发布时间】:2015-10-06 03:32:30
【问题描述】:

我有两个模型,一个存储事件,另一个是日历模型的连接模型。加入模型与远程日历 API 集成,并在创建或删除时通过 API 调用(通过 before_save 和 before_destroy 回调)进行管理。

这在一次删除一个连接模型记录时效果很好,但由于我依赖于::destroy 在我的事件模型中定义的 has_many 关联(我不希望远程日历中的孤立事件),删除单个event 将导致 N api 调用(其中 N 是连接模型记录的数量,假设可能是数千),这很容易导致超时。

理想情况下,我想延迟 CalendarEvent.destroy 调用,但仅限于从删除事件中调用它时。例如:

Event.destroy [对所有 CalendarEvent (join model) 记录调用 delay.destroy] CalendarEvent.destroy [销毁不带延迟作业]

有没有办法通过 has_many 调用来延迟这个?

有没有办法通过依赖传递自定义销毁方法:?

有没有办法在 CalendarEvent.destroy 方法中知道它是否是从依赖的: :destroy 定义中调用的?

【问题讨论】:

  • 我会通过 sidekiq 之类的方式启动删除作业。

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord delayed-job


【解决方案1】:

有没有办法通过 has_many 调用延迟这个?

没有。


有没有办法通过依赖传递自定义销毁方法:?

也没有; the docs 中只有少数几个被接受的选项:

:destroy 导致所有关联的对象也被销毁。

:delete_all 会直接从数据库中删除所有关联的对象(因此不会执行回调)。

:nullify 导致外键设置为 NULL。不执行回调。

:restrict_with_exception 会在有任何关联记录时引发异常。

:restrict_with_error 如果有任何关联对象,则会将错误添加到所有者。


有没有办法在 CalendarEvent.destroy 方法中知道它是否是从依赖的: :destroy 定义中调用的?

从技术上讲,您可以在 caller 中嗅探堆栈跟踪,但这听起来不是一个好主意。


我将删除 dependent: 选项并构建一个自定义 after_destroy 以延迟方式清理 CalendarEvent 对象。

after_destroy :destroy_calendar_events

def destroy_calendar_events
  calendar_events.each {|event| event.delay.destroy }
end

很久以前,我工作的一家公司用Async Observer 解决了这个问题,它提供了async_destroy 方法,但它基于Beanstalk,需要进行调整。如果你想加入一些东西,代码可能会很有趣。

【讨论】:

  • 关闭 - 可能在数据库中没有外键的情况下可以工作(没有依赖的外键: :destroy 不想工作)。不适用于我的特定情况,但我会赞成这个答案,因为它可能适用于其他没有 FK 问题的人。
【解决方案2】:

Kristján 的回答可能会奏效,但我遇到了外键问题。没有依赖的外键: :destroy 不想工作。

然后,如果我尝试延迟 API 服务对象本身,我会遇到 delay_job 的问题,我希望将延迟作业的 RSA 密钥存储在 DB 条目中。哎呀。无论如何它都没有验证(空)密钥,所以它在两个方面都失败了。

然后我尝试在创建新 API 对象的对象实例上延迟一个方法。当然它失败了,因为延迟的作业试图加载对象(已经销毁)来运行作业。拍打额头,尖叫,拉头发,重复。

最终工作的是调用一个类方法(或者它实际上可以是一个外部服务对象等),只传入字符串(不指望任何可以删除的对象来提供必要的信息)。

class CalendarEvent

  # bunch o' stuff

  def destroy
    self.class.delay.remove_remote_event!(user, remote_calendar_id, remote_id)
  super
  end

  def self.remove_remote_event!(user, remote_calendar_id, remote_id)
    EpGoogle::Interfaces::Calendar.new(user.email).delete_event(remote_calendar_id, remote_id)
  end

end

...然后DelayedJob就可以成功运行任务了。

它没有我想的那么棒,因为如果作业失败,远程事件可能会被孤立,但对于我的用例来说已经足够了。

【讨论】:

  • 啊,外键约束很有意义。老实说,虽然 DBA 大神肯定会放弃我的来世,但我在一年前就停止使用外键约束,转而支持代码和测试,并且没有回头。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 2012-07-16
  • 2012-01-15
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
相关资源
最近更新 更多