dependent: :destroy - 我可以用 Delayed Job 延迟这个吗?

dependent: :destroy - can I delay this with Delayed Job?

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

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

理想情况下,我想延迟 CalendarEvent.destroy 调用,但只有在删除事件时才调用它。例如:

Event.destroy [对所有 CalendarEvent(加入模型)记录调用 delay.destroy] CalendarEvent.destroy [销毁无delayed_job]

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

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

有没有办法,在 CalendarEvent.destroy 方法中,知道它是否被从属调用::destroy 定义?

Is there a way to delay this through the has_many call?

没有。


Is there a way to pass a custom destroy method through dependent:?

也没有; the docs:

中只有少数可接受的选项

:destroy causes all the associated objects to also be destroyed.

:delete_all causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).

:nullify causes the foreign keys to be set to NULL. Callbacks are not executed.

:restrict_with_exception causes an exception to be raised if there are any associated records.

:restrict_with_error causes an error to be added to the owner if there are any associated objects.


Is there a way, in the CalendarEvent.destroy method, to know whether or not it's being called from a dependent: :destroy definition?

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


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

after_destroy :destroy_calendar_events

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

很久以前,我工作的一家公司用 Async Observer, which provided an async_destroy method, but that was based on Beanstalk 解决了这个问题,需要进行调整。如果您想加入一些东西,代码可能会很有趣。

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

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

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

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

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 能够 运行 任务成功。

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