Serializza gli invii SMTP su una coda unica per non far chiudere Aruba.
CI / scan_js (push) Has been cancelled
CI / scan_ruby (push) Has been cancelled
CI / lint (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-07 12:06:23 +02:00
co-authored by Cursor
parent a9470d7e77
commit 403fe80d37
12 changed files with 423 additions and 29 deletions
+98
View File
@@ -0,0 +1,98 @@
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