Rails 6、如何保证一个Mailer不会重试,例如隐式(非显式)使用Active job时,如何添加ActiveJob retry: false设置?

In Rails 6, how ensure a Mailer will not retry, eg, how add ActiveJob retry: false setting when Active job is used implicitly (not explicitly)?

在新的 Rails 6.1 应用程序中,我想明确禁用邮件作业的任何重试。

由于 ActionMailer 自动 使用 ActiveJob,我如何为特定邮件程序添加自定义 ActiveJob 配置 class,例如禁用 sidekiq 重试?

如果使用了 显式 ActiveJob class,很简单:

class ExampleJob < ActiveJob::Base
  sidekiq_options retry: false # custom setting allowed here in a JOB

  def perform(*args)
    # Perform Job
  end
end

但是在像 ActionMailer 这样使用 ActiveJob 的情况下隐式(“幕后”)如何明确禁用 sidekiq 重试邮件作业?

例如,发送这样的电子邮件时:

AccountInvitationsMailer.with(account_invitation: self).invite.deliver_later

当当前邮件代码为:

class ApplicationMailer < ActionMailer::Base
  default from: "someone@example.com"
  layout "mailer"

  # Include any view helpers from your main app to use in mailers here
  helper ApplicationHelper
end


class AccountInvitationsMailer < ApplicationMailer
  def invite
    @account_invitation = params[:account_invitation]
    @account = @account_invitation.account
    @invited_by = @account_invitation.invited_by

    name = @account_invitation.name
    email = @account_invitation.email

    mail(
      to: "#{name} <#{email}>",
      from: "#{@invited_by.name} <invites@example.com>",
      subject: t(".subject", inviter: @invited_by.name, account: @account.name)
    )
  end
end

实际上,您可以明确设置要在 ActionMailer 上使用的 ActiveJob class,例如:

class ApplicationMailer < ActionMailer::Base
  self.delivery_job = ExampleJob

  # blah blah blah
end

因此您稍后可以在 ActiveJob class 中设置任何其他内容。

如果有人看到 Rails 版本 >= 6(也在 7 中测试过),这里有一个更详细的 ExampleJob 实现。

考虑上面的例子和这样的调用:

AccountInvitationsMailer.with(some: 'params').invite.deliver_later

ExampleJob class 应该类似于:

class ExampleJob < ApplicationJob
  # rescue_from StandardError, with: :notify_to_exception_tracker
  # Any other customization option

  # Perform will receive 4 params
  # mailer: The original mailer class, i.e. `AccountInvitationsMailer`
  # mailer_method: The method called in the mailer, i.e. `invite`
  # delivery_method: The delivery method used, i.e. `deliver_later`
  # args: Original arguments sent to the mailer wrapped in params container, i.e. `{ params: { some: 'params' } }`
  def perform(mailer, mail_method, delivery_method, args)
    klass = mailer.constantize
    klass.with(args[:params]).public_send(mail_method).send(delivery_method)
  end

  # ...
end

最后,原始问题的另一种替代方法是通过在相关环境文件(开发、暂存、生产等)中设置 config.action_mailer.raise_delivery_errors = false 来忽略交付错误