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