Search code examples
carrierwaveruby-on-rails-4sidekiq

Sidekiq repeating the same job over and over


I am essentially writing the project in Railscast 383 - the second part, when the photo is uploaded directly to AWS S3, and the photo is then processed in the background by Sidekiq to create a thumbnail version of the photo. I'm on Rails 4.

My issue is that the Sidekiq job, after completing successfully, keeps repeating over and over, instead of just stopping.

Where am I going wrong? I can't see any difference between my code and that of the Railscast, other than I'm on Rails 4 (so strong parameters instead of attr_accessible)

Photo class:

class Photo < ActiveRecord::Base
  mount_uploader :image, ImageUploader

  default_scope order('updated_at DESC')

  after_save :enqueue_image

  def image_name
    File.basename(image.path || image.filename) if image
  end

  def enqueue_image
    ImageWorker.perform_async(id, key) if key.present?
  end
end

ImageWorker:

class ImageWorker
  include Sidekiq::Worker

  sidekiq_options retry: false
  # sidekiq_options retry: 3

  def perform(id, key)
    photo = Photo.find(id)
    photo.key = key
    photo.remote_image_url = photo.image.direct_fog_url(with_path: true)
    photo.save!
    photo.update_column(:image_processed, true)
  end
end

Uploader:

class ImageUploader < CarrierWave::Uploader::Base

  include CarrierWaveDirect::Uploader
  include CarrierWave::RMagick

  # storage :fog
  #### storage defaults to fog when CarrierWaveDirect::Uploader is included ####

  include CarrierWave::MimeTypes
  process :set_content_type

  version :thumb do
    process :resize_to_limit => [200, 200]
  end

  version :medium do
    process :resize_to_limit => [400, 400]
  end

end

Solution

  • One reason for the sidekiq worker to be called again and again is because the perform_async is called every time you save your photo object, which occurs within the sidekiq worker itself. So every time the ImageWorker is called, it saves the photo, calling the ImageWorker again, creating the loop you are experiencing.

    Are you sure you aren't missing a check for the :image_processed tag to be true before calling the ImageWorker again. Try this:

    def enqueue_image
        ImageWorker.perform_async(id, key) if key.present? && !image_processed
    end
    

    This will check if the image was processed once before. I think this was probably meant to be set in the rails cast but the author forgot about it, otherwise the image_processed flag is obsolete.