当推入序列化字段时,sidekiq 的奇怪行为在邮件程序上返回了 jid

Odd behaviour with sidekiq returned jid on a mailer when pushing into a serialised field

当我试图将 worker 返回的 jid 推送到模型上的序列化字段时,我遇到了一些奇怪的行为。此代码的最佳解释:

class Foo < ActiveRecord::Base
  serialize :jids, Array

  def do_job
    test_jids = []

    jids << "s"
    test_jids << "s"
    Rails.logger.info("s has been pushed")
    Rails.logger.info(jids)
    Rails.logger.info(test_jids)

    jids << BarWorker.perform_in(10.minutes, self.id)
    test_jids << BarWorker.perform_in(10.minutes, self.id)
    Rails.logger.info("normal worker has been pushed")
    Rails.logger.info(jids)
    Rails.logger.info(test_jids)

    jids << FooMailer.delay_for(10.minutes).confirm_foo(self)
    test_jids << FooMailer.delay_for(10.minutes).confirm_foo(self)
    Rails.logger.info("mailer worker id pushed")
    Rails.logger.info(jids)
    Rails.logger.info(test_jids)

    jids
  end
end

预期日志记录:

s has been pushed
["s"]
["s"]

normal worker has been pushed
["s", "6d25faf98b6448a43e1d3bb5"]
["s", "7144ceab11ece07ae352ffb9"]

mailer worker id pushed
["s", "6d25faf98b6448a43e1d3bb5", "54c4634bfc11ec1adad926da"]
["s", "7144ceab11ece07ae352ffb9", "3d0bb94e0411a04ab0339f68"]

实际记录。

s has been pushed
["s"]
["s"]

normal worker has been pushed
["s", "243675806204134e6f05abaa"]
["s", "1444f558d7582df1000c6f4b"]

mailer worker id pushed
["s", "243675806204134e6f05abaa"]
["s", "1444f558d7582df1000c6f4b", "c0878213a8ea5f783c30e666"]

邮件程序的作业 ID 尚未推送到 foo 上的 jids 属性中。

在混乱中,我有点 hacky 并在 Foo

上写了这个方法
def do_job_with_assignment
  test_jids = []

  jids << "s"
  test_jids << "s"
  Rails.logger.info("s has been pushed")
  Rails.logger.info(jids)
  Rails.logger.info(test_jids)


  bar_worker_jid = BarWorker.perform_in(10.minutes, self.id)
  jids << bar_worker_jid

  test_bar_worker_jid = BarWorker.perform_in(10.minutes, self.id)
  test_jids << test_bar_worker_jid
  Rails.logger.info("normal worker has been pushed")
  Rails.logger.info(jids)
  Rails.logger.info(test_jids)

  foo_mailer_jid = FooMailer.delay_for(10.minutes).confirm_foo(self)
  jids << foo_mailer_jid

  test_foo_mailer_jid = FooMailer.delay_for(10.minutes).confirm_foo(self)
  test_jids << test_foo_mailer_jid
  Rails.logger.info("mailer worker id pushed")
  Rails.logger.info(jids)
  Rails.logger.info(test_jids)

  jids
end

现在 确实 产生了预期的日志记录。

除了显而易见的,两者之间还有什么区别?

编辑:

没有人领取赏金,我想更进一步,但我不确定问题出在哪里。有人建议好的起点吗?

编辑:类

> Foo.new.jids.class
=> Array

> BarWorker.perform_in(10.minutes, 1).class
=> String 

> FooMailer.delay_for(10.minutes).confirm_foo(self).class
=> String

编辑:

演示应用程序。 https://github.com/richkettle/sidekiq-oddities

这是我的分析...

jidsFoo class 成员...以及 sidekiq 在后台工作的方式(使用 celluloid 功能不允许竞争条件并避免死锁和一堆我不知道的其他功能)..让它在后台线程中完成大部分工作...

如果我的分析是正确的...那么日志记录可能如下所示

foo = Foo.new
foo.do_job

# logs show the following
["s", "243675806204134e6f05abaa"]
["s", "1444f558d7582df1000c6f4b", "c0878213a8ea5f783c30e666"]

foo.jids
=>["s", "243675806204134e6f05abaa"]

foo.reload
foo.jids
=>["s", "243675806204134e6f05abaa", "I'm new here"]

虽然 test_jids 是一个本地数组,可能会在方法结束之前使用...这就是它立即返回的原因..(这就是为什么您的解决方法有效的原因:D 有事可做线程的工作方式)...

如有错误请指正... 如果我没记错的话...您需要更多详细信息...您必须深入了解 sidkiq 和 celluloid 如何管理它们的线程

也看看下面的

https://github.com/celluloid/celluloid/wiki/Gotchas

我认为这是因为 Rails 缓存了除序列化字段之外的所有列类型的属性。考虑到这一点,当您在 getter 上为 jids 调用 << 方法时,Rails 会获取该字段的属性,但是当您将 self 传递给邮件程序在 Sidekiq 中序列化时再次调用 getter,并覆盖属性实例,因此返回值不会添加到正确的 jids 实例中。

我不确定我是否完全理解为什么会发生这种情况,所以一些具体的实施细节可能不太准确!

这可以 fixed/made 通过以下方式之一使用此版本的 Rails (4.1.5):

缓存 jids 属性:

class Foo < ActiveRecord::Base
  serialize :jids, Array
  cache_attributes :jids

使用 jids setter:

jids = self.jids << FooMailer.delay_for(10.minutes).confirm_foo(self)

id 或副本传递到邮件程序中:

jids << FooMailer.delay_for(10.minutes).confirm_foo(id)
# or
jids << FooMailer.delay_for(10.minutes).confirm_foo(self.dup)

这在 Rails 4.2 中已修复,当此代码被重写时,我可以找到一个 PR:https://github.com/rails/rails/pull/15429