はじめに
app/mailers/test_mailer.rb
class TestMailer < ApplicationMailer
def sample_email(user)
@user = user
mail(to: @user.email_address, subject: 'Sample Email')
end
end
app/jobs/delivery_test_mailer_job.rb
class TestMailerBulkSendJob < ApplicationJob
def perform
users = User.all # ここは適当な条件
# メールをエンキューする処理
end
end
上記のように、メールを複数件送信する場合、エンキューする処理をどのように書きますか?
こんな方法あるよなを書きつつ、Rails8.1で追加されたメソッドについて紹介します。
書き方
素直にループしてキューイングする
app/jobs/delivery_test_mailer_job.rb
class TestMailerBulkSendJob < ApplicationJob
def perform
users = User.all # ここは適当な条件
users.each do |user|
TestMailer.sample_email(user).deliver_later
end
end
end
シンプルですね。
件数が数千件になってくると、1件ずつエンキューするのはパフォーマンス的に影響が出てきそうです。
ActiveJob.perform_all_later を利用する
Rails 7.1にて追加されたActiveJob.perform_all_later を使って、複数件のメールを一括でエンキューすることができます。
app/jobs/delivery_test_mailer_job.rb
class TestMailerBulkSendJob < ApplicationJob
def perform
users = User.all # ここは適当な条件
delivery_jobs = []
users.each do |user|
delivery_jobs << ActionMailer::MailDeliveryJob.new(
'TestMailer',
'sample_email',
'deliver_now',
args: [admin_user]
)
end
ActiveJob.perform_all_later(delivery_jobs)
end
end
ActionMailer::MailDeliveryJobのObjectを明示して生成する必要があるため、若干の冗長さがあります。
ActionMailer.deliver_all_later を利用する
Rails8.1からは、以下のように書くことができるようになりました。
app/jobs/delivery_test_mailer_job.rb
class TestMailerBulkSendJob < ApplicationJob
def perform
users = User.all # ここは適当な条件
deliveries = []
users.each { |user| deliveries << TestMailer.sample_email(user) }
ActionMailer.deliver_all_later(deliveries)
end
end
実装を見てみると、ActiveJob.perform_all_later を使っていて、前述した書き方のラッパーのようなイメージです。
パフォーマンス的には大きく変わることはなさそうですが、だいぶスッキリかけるようになった印象があります。
ActionMailerを使っていて、大量件数のメール送信をしている箇所があれば、積極的に使っていきたいです。