99 lines
2.3 KiB
Ruby
99 lines
2.3 KiB
Ruby
class Mailings::OutboundQueue
|
|
LOCK_PATH = Rails.root.join("tmp/outbound_smtp.lock")
|
|
STAMP_PATH = Rails.root.join("tmp/outbound_last_sent")
|
|
|
|
class << self
|
|
attr_accessor :min_interval
|
|
|
|
def enqueue(recipient)
|
|
recipient.with_lock do
|
|
return false unless recipient.mailing.sending?
|
|
return false unless recipient.pending?
|
|
|
|
recipient.update!(status: "queued", error_message: nil)
|
|
true
|
|
end
|
|
end
|
|
|
|
def drain_one!
|
|
File.open(lock_path, File::RDWR | File::CREAT, 0o644) do |file|
|
|
return :busy unless file.flock(File::LOCK_EX | File::LOCK_NB)
|
|
|
|
wait = seconds_until_next_slot
|
|
return :wait if wait.positive?
|
|
|
|
recipient = next_queued
|
|
return :empty if recipient.nil?
|
|
return :closed unless Mailings::SendClock.new(recipient.mailing).open?
|
|
|
|
begin
|
|
CampaignMailer.raise_delivery_errors = true
|
|
outcome = recipient.deliver_queued!
|
|
stamp!
|
|
outcome == :sent ? :sent : :deferred
|
|
ensure
|
|
CampaignMailer.raise_delivery_errors = false if Rails.env.development?
|
|
end
|
|
end
|
|
end
|
|
|
|
def exclusive
|
|
File.open(lock_path, File::RDWR | File::CREAT, 0o644) do |file|
|
|
file.flock(File::LOCK_EX)
|
|
yield
|
|
end
|
|
end
|
|
|
|
def next_queued
|
|
queued_scope.order(:updated_at, :id).first
|
|
end
|
|
|
|
def queued_scope
|
|
MailingRecipient.queued.joins(:mailing).merge(Mailing.sending)
|
|
end
|
|
|
|
def seconds_until_next_slot
|
|
return 0 if min_interval.to_f <= 0
|
|
|
|
last = last_sent_at
|
|
return 0 if last.nil?
|
|
|
|
remaining = min_interval - (Time.current - last)
|
|
remaining.positive? ? remaining : 0
|
|
end
|
|
|
|
def reset!
|
|
@last_sent_at = nil
|
|
File.delete(stamp_path) if File.exist?(stamp_path)
|
|
rescue Errno::ENOENT
|
|
nil
|
|
end
|
|
|
|
def stamp!
|
|
@last_sent_at = Time.current
|
|
File.write(stamp_path, @last_sent_at.to_f.to_s)
|
|
end
|
|
|
|
def last_sent_at
|
|
return @last_sent_at if @last_sent_at
|
|
return unless File.exist?(stamp_path)
|
|
|
|
@last_sent_at = Time.zone.at(Float(File.read(stamp_path)))
|
|
rescue ArgumentError, TypeError, Errno::ENOENT
|
|
nil
|
|
end
|
|
|
|
private
|
|
|
|
def lock_path
|
|
LOCK_PATH
|
|
end
|
|
|
|
def stamp_path
|
|
STAMP_PATH
|
|
end
|
|
end
|
|
|
|
self.min_interval = Rails.env.test? ? 0.0 : 180.0
|
|
end
|