discourse/app/jobs/regular/notify_mailing_list_subscribers.rb
Martin Brennan b3d3ad250b
FIX: Handle SMTPServerBusy for group smtp email (#13632)
Use the `sidekiq_retry_in` code from Jobs::UserEmail in group SMTP. Also we don't need to keep `seconds_to_delay` -- sidekiq uses the default delay calculation if you return 0 or nil from the block. See 3330df0ee3/lib/sidekiq/job_retry.rb (L216-L234) for sidekiq default retry delay logic.

I experimented with extracting this into a concern or a module, but `sidekiq_retry_in` is quite magic and it would not allow me to abstract away into a module that calls some method specificall in the child job class.

I would love to write tests for this, but it does not seem possible (not sure if its because of our test
setup) to write tests that test sidekiq's retry capability, and I am not sure if we should be anyway. Initial addition
to UserEmail did not test this functionality 
d224966a0e
2021-07-06 13:37:52 +10:00

126 lines
4.2 KiB
Ruby

# frozen_string_literal: true
module Jobs
class NotifyMailingListSubscribers < ::Jobs::Base
include Skippable
RETRY_TIMES = [5.minute, 15.minute, 30.minute, 45.minute, 90.minute, 180.minute, 300.minute]
sidekiq_options queue: 'low'
sidekiq_options retry: RETRY_TIMES.size
sidekiq_retry_in do |count, exception|
# returning nil/0 will trigger the default sidekiq
# retry formula
#
# See https://github.com/mperham/sidekiq/blob/3330df0ee37cfd3e0cd3ef01e3e66b584b99d488/lib/sidekiq/job_retry.rb#L216-L234
case exception.wrapped
when SocketError
return RETRY_TIMES[count]
end
end
def execute(args)
return if SiteSetting.disable_mailing_list_mode
post_id = args[:post_id]
post = post_id ? Post.with_deleted.find_by(id: post_id) : nil
return if !post || post.trashed? || post.user_deleted? || !post.topic || post.raw.blank?
users =
User.activated.not_silenced.not_suspended.real
.joins(:user_option)
.where('user_options.mailing_list_mode AND user_options.mailing_list_mode_frequency > 0')
.where('NOT EXISTS (
SELECT 1
FROM muted_users mu
WHERE mu.muted_user_id = ? AND mu.user_id = users.id
)', post.user_id)
.where('NOT EXISTS (
SELECT 1
FROM ignored_users iu
WHERE iu.ignored_user_id = ? AND iu.user_id = users.id
)', post.user_id)
.where('NOT EXISTS (
SELECT 1
FROM topic_users tu
WHERE tu.topic_id = ? AND tu.user_id = users.id AND tu.notification_level = ?
)', post.topic_id, TopicUser.notification_levels[:muted])
.where('NOT EXISTS (
SELECT 1
FROM category_users cu
WHERE cu.category_id = ? AND cu.user_id = users.id AND cu.notification_level = ?
)', post.topic.category_id, CategoryUser.notification_levels[:muted])
if SiteSetting.tagging_enabled?
users = users.where('NOT EXISTS (
SELECT 1
FROM tag_users tu
WHERE tu.tag_id in (:tag_ids) AND tu.user_id = users.id AND tu.notification_level = :muted
)', tag_ids: post.topic.tag_ids, muted: TagUser.notification_levels[:muted])
end
if SiteSetting.must_approve_users
users = users.where(approved: true)
end
if SiteSetting.mute_all_categories_by_default
users = users.watching_topic_when_mute_categories_by_default(post.topic)
end
DiscourseEvent.trigger(:notify_mailing_list_subscribers, users, post)
users.find_each do |user|
if Guardian.new(user).can_see?(post)
if EmailLog.reached_max_emails?(user)
skip(user.email, user.id, post.id,
SkippedEmailLog.reason_types[:exceeded_emails_limit]
)
next
end
if user.user_stat.bounce_score >= SiteSetting.bounce_score_threshold
skip(user.email, user.id, post.id,
SkippedEmailLog.reason_types[:exceeded_bounces_limit]
)
next
end
if (user.id == post.user_id) && (user.user_option.mailing_list_mode_frequency == 2)
skip(user.email, user.id, post.id,
SkippedEmailLog.reason_types[:mailing_list_no_echo_mode]
)
next
end
begin
if message = UserNotifications.mailing_list_notify(user, post)
EmailLog.unique_email_per_post(post, user) do
Email::Sender.new(message, :mailing_list, user).send
end
end
rescue => e
Discourse.handle_job_exception(e, error_context(args, "Sending post to mailing list subscribers", user_id: user.id, user_email: user.email))
end
end
end
end
def skip(to_address, user_id, post_id, reason_type)
create_skipped_email_log(
email_type: 'mailing_list',
to_address: to_address,
user_id: user_id,
post_id: post_id,
reason_type: reason_type
)
end
end
end