在 Rails 6 中同时或循环发送多封电子邮件的最佳实践?

问题描述

我有一个 rake 任务,它循环遍历预订,并使用 .deliver 方法(我从 here 那里得到的)为每个人发送一封电子邮件我有意识 现在 7 岁了).

问题是,有时有些电子邮件没有发送出去。这是我的代码

# Select bookings starting soon
bookings = Booking.where('start_time < ?',24.hours.since)

# Email a reminder 
bookings.each do |booking|

  customer = booking.customer

  CustomerMailer.reminder_24h(customer,booking).deliver

end

由于循环处于 rake 任务中,我认为调用 .deliver_later 没有任何价值,所以我只使用 .deliver 就像在 rails cast 中一样

我很想知道是否有有助于使用 Action Mailer 的最佳实践,例如每封电子邮件之间是否应该有一个 sleep 2?或者我应该总是使用 .deliver_later 来减轻服务器上的负载?是否有任何其他与 Rails 相关的原因导致我的代码无法运行(或者,更糟糕的是,我使用了任何应该重构的反模式?)

TL;DR 为什么像上面的代码那样循环发送的电子邮件有时会发送失败

解决方法

没有答案,但来自其他论坛的一些建议。

发送电子邮件是一个充满潜在故障的过程。在后台作业中执行此操作始终是个好主意,该作业可以在出现网络等间歇性错误时重新尝试,也可以因地址错误而跳过。

以下是可能起作用的草图:

# Reminder process rake task
namespace :bookings do
  desc "Deliver reminders to upcoming bookings"
  task remind_upcoming: :environment do
    EnqueueUpcomingBookingReminders.call(UpcomingBookingRemindersQuery.call)
  end
end
class EnqueueUpcomingBookingReminders
  def self.call(bookings_scope)
    booking_communication_attrs =
      bookings_scope
        .pluck(:id)
        .map { |id| {booking_id: id,type: "reminder"} }
    communications_result = 
      BookingCommunication.insert_all(booking_communication_attrs,unique_by: %i[booking_id type])
      # Email a reminder 
    communications_result.rows.flatten.each do |communication_id|
      DeliverBookingCommunicationJob.perform_later(communication_id)
    end
  end
end
class UpcomingBookingRemindersQuery
  def self.call(scope: Booking)
    Booking
      .upcoming_this_day
      .left_outer_joins(:communications)
      .merge(BookingCommunication.reminder)
      .where(communications: {id: nil})
  end
end
class Booking
  has_many :communications,class_name: "BookingCommunication"
  def self.upcoming_this_day
    where(starts_at:,(Time.current..24.hours.from_now))
  end
end
class BookingCommunication
  belongs_to :booking
  enum step: {confirmation: "confirmation",reminder: "reminder"} # combination of this and the booking id should be unique
  enum status: {pending: "pending",delivered: "delivered",canceled: "canceled",failed: "failed"} # should default to pending at database layer
end
class DeliverBookingCommunicationJob < ApplicationJob
  def perform(communication_id)
    communication = BookingCommunication.find_by(communication_id)
    # Guard against state that invalidates us running this job
    return unless communication
    return unless communication.pending?
    return communication.canceled! if communication.booking.canceled? # This should probably live in the cancel booking process
    booking = communication.booking
    mailer = CustomerMailer.with(customer: booking.customer,booking: booking)
    case communication.step
    when "reminder"
      mailer.reminder_24h.deliver_now
    else
      # log unknown communication step,send to error tracking but dont raise since we do not want job to run again
    end
    communication.delivered!
  rescue SomeEmailRelatedError => err
    communication.failed!
    # deliver err to error tracking service
  end
end

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...