当作业重试时,Sidekiq/Redis 在哪里或如何存储对象?

Where or how does Sidekiq/Redis store an object when a Job is retrying?

我们为我们的客户提供了一种方法来预览发送给他们用户的大量电子邮件。对于某些电子邮件,我们模拟(OpenStruct)一些对象并在电子邮件视图中使用它们:

用户邮箱: class UserMailer < ApplicationMailer

 def about_to_expire_card_reminder
    ...
    if params[:email_preview]
      @subject += " (email de prueba)"
      @user_default_credit_card = mock_credit_card
    end

    send_email(subject: @subject)
  end
    

  def mock_credit_card
    OpenStruct.new(last_four_digits: "1234 (tarjeta de prueba)",
                   expiration_date: 10.days.from_now.to_date)
  end

end

我们今天强制错误并注意到 mock_credit_card returns 的 OpenStruct 没有出现在重试作业显示的 Sidekiq 管理页面中。

正在重试作业信息:

Next Retry: in 3 minutes
Retry Count: 4
Queue: mailers
Job: ActionMailer::MailDeliveryJob
Arguments: "UserMailer", "about_to_expire_card_reminder", "deliver_now", {"params"=>{"user"=>{"_aj_globalid"=>"gid://database/User/1"}, "to"=>info@testemail.com", "email_preview"=>true, "aj_symbol_keys"=>["user,"to","email_preview"]}, "args"=>[],"_aj_symbol_keys"=>["params","args"]}
Error: ActionView::Template:Error: undefined method "last_four_digits" for #struct expiration_date="2021-03-31">

Sidekiq 在哪里存储模拟的 OpenStruct 对象?连载了吗?在作业完成或删除之前是否占用内存?

如有任何见解,我们将不胜感激。如果您需要更多代码,请告诉我。

TL;DR 未存储模拟对象。 sidekiq 存储的唯一内容是您打印为“重试作业信息:”的内容。

长版...它是如何工作的?

ActiveJob 使用作业名称和参数将 sidekiq 中的每个作业排入队列。如果作业失败,它会序列化(原始)作业信息,以便重试失败的作业。您可以在重试作业信息中看到作业 class 名称和参数:

Job: ActionMailer::MailDeliveryJob
Arguments: "UserMailer", "about_to_expire_card_reminder", "deliver_now", {"params"=>{"user"=>{"_aj_globalid"=>"gid://database/User/1"}, "to"=>info@testemail.com", "email_preview"=>true, "aj_symbol_keys"=>["user,"to","email_preview"]}, "args"=>[],"_aj_symbol_keys"=>["params","args"]}

连同 sidekiq 需要的其他信息(它应该 运行 的队列和重试次数)。

当 sidekiq 运行 作业时,它 constantizes 作业 class 并调用它的实例执行,传入它存储的参数。在这种情况下,它会像这样调用 MailDeliveryJob#perform

ActionMailer::MailDeliveryJob.new.perform("UserMailer", "about_to_expire_card_reminder", "deliver_now", {"params"=>{"user"=>{"_aj_globalid"=>"gid://database/User/1"}, "to"=>info@testemail.com", "email_preview"=>true, "aj_symbol_keys"=>["user,"to","email_preview"]}, "args"=>[],"_aj_symbol_keys"=>["params","args"]})

Activejob 基础 class 对作业参数进行处理(将 {"_aj_globalid"=>"gid://database/User/1"} 转换为 User.find(1)),然后 MailDeliveryJob#perform 方法调用您的邮件程序 class像这样:

UserMailer.with({"user"=> User.find(1), "to"=>info@testemail.com", "email_preview"=>true}).about_to_expire_card_reminder.deliver_now

这些是存储在 sidekiq 中的相同参数,只是用户被夸大了。此时 about_to_expire_card_reminder 方法创建了一个新的模拟信用卡对象,将其存储在邮件程序的一个实例变量中 class,并且邮件对象被构建为可以访问这些实例变量。

让我知道这是否有意义/如果这回答了问题。