Two Sidekiq jobs with different failure philosophies: one defensively logs and swallows errors (image processing shouldn't crash the request that triggered it), the other declares typed retry/discard rules for a flaky remote fetch.
# app/jobs/process_location_image_job.rb
class ProcessLocationImageJob < ApplicationJob
queue_as :media
def perform(event_location_id)
event_location = EventLocation.find_by(id: event_location_id)
unless event_location
Rails.logger.warn("[ProcessLocationImageJob] EventLocation not found: ID #{event_location_id}")
return
end
unless event_location.location_image.attached?
Rails.logger.warn("[ProcessLocationImageJob] No image attached for EventLocation ID #{event_location.id}")
return
end
begin
variant = event_location.location_image.variant(resize: "640x480").processed
variant.open do |file|
event_location.location_image.attach(
io: file,
filename: event_location.location_image.filename.to_s,
content_type: event_location.location_image.content_type
)
end
Rails.logger.info("[ProcessLocationImageJob] Successfully resized and re-attached image for EventLocation ID #{event_location.id}")
rescue => e
Rails.logger.error("[ProcessLocationImageJob] Failed to process image for EventLocation ID #{event_location.id}: #{e.message}")
Rails.logger.error(e.backtrace.join("\n"))
end
end
end
Resizes via an ActiveStorage variant (libvips-backed) and re-attaches in place, so callers just keep referencing the same attachment.
# app/jobs/fetch_remote_avatar_job.rb
require "open-uri"
class FetchRemoteAvatarJob < ApplicationJob
queue_as :default
retry_on OpenURI::HTTPError, Timeout::Error, SocketError, URI::InvalidURIError,
wait: 10.seconds, attempts: 3
discard_on ActiveRecord::RecordNotFound
def perform(amigo_id, source:, url: nil)
amigo = Amigo.find(amigo_id)
return if amigo.avatar_source == "upload"
ok =
case source
when "gravatar" then amigo.attach_gravatar
when "url" then (u = url.presence || amigo.avatar_remote_url) && amigo.attach_remote_image(u)
else false
end
if ok
amigo.send(:process_avatar)
amigo.touch(:avatar_synced_at)
elsif !amigo.avatar.attached?
amigo.attach_default_avatar
end
end
end
Declarative retry_on/discard_on instead of a hand-rolled rescue block — transient network errors get three attempts, a deleted amigo just gets discarded.# app/jobs/fetch_remote_avatar_job.rb
require "open-uri"
class FetchRemoteAvatarJob < ApplicationJob
queue_as :default
retry_on OpenURI::HTTPError, Timeout::Error, SocketError, URI::InvalidURIError,
wait: 10.seconds, attempts: 3
discard_on ActiveRecord::RecordNotFound
def perform(amigo_id, source:, url: nil)
amigo = Amigo.find(amigo_id)
return if amigo.avatar_source == "upload"
ok =
case source
when "gravatar" then amigo.attach_gravatar
when "url" then (u = url.presence || amigo.avatar_remote_url) && amigo.attach_remote_image(u)
else false
end
if ok
amigo.send(:process_avatar)
amigo.touch(:avatar_synced_at)
elsif !amigo.avatar.attached?
amigo.attach_default_avatar
end
end
end
# app/jobs/fetch_remote_avatar_job.rb
require "open-uri"
class FetchRemoteAvatarJob < ApplicationJob
queue_as :default
retry_on OpenURI::HTTPError, Timeout::Error, SocketError, URI::InvalidURIError,
wait: 10.seconds, attempts: 3
discard_on ActiveRecord::RecordNotFound
def perform(amigo_id, source:, url: nil)
amigo = Amigo.find(amigo_id)
return if amigo.avatar_source == "upload"
ok =
case source
when "gravatar" then amigo.attach_gravatar
when "url" then (u = url.presence || amigo.avatar_remote_url) && amigo.attach_remote_image(u)
else false
end
if ok
amigo.send(:process_avatar)
amigo.touch(:avatar_synced_at)
elsif !amigo.avatar.attached?
amigo.attach_default_avatar
end
end
end
# app/jobs/fetch_remote_avatar_job.rb
require "open-uri"
class FetchRemoteAvatarJob < ApplicationJob
queue_as :default
retry_on OpenURI::HTTPError, Timeout::Error, SocketError, URI::InvalidURIError,
wait: 10.seconds, attempts: 3
discard_on ActiveRecord::RecordNotFound
def perform(amigo_id, source:, url: nil)
amigo = Amigo.find(amigo_id)
return if amigo.avatar_source == "upload"
ok =
case source
when "gravatar" then amigo.attach_gravatar
when "url" then (u = url.presence || amigo.avatar_remote_url) && amigo.attach_remote_image(u)
else false
end
if ok
amigo.send(:process_avatar)
amigo.touch(:avatar_synced_at)
elsif !amigo.avatar.attached?
amigo.attach_default_avatar
end
end
end