Search code examples
rubycarrierwavesidekiq

Is Carrierwave thread-safe?


I'm using Sidekiq and Carrierwave to upload images to S3. The background job does the following things:

  1. Downloads image from a remote url;
  2. Resizes it using minimagick;
  3. Uploads the resized images to S3.

Here's the code snippet:

  def store_for_mobile(file)
     self.class.process optimize: [1080, 810]
     %w(android ios).each do |device|
        @directory = File.join('banners/mobile', device)
        store!(file)
     end
  end

  def store_for_web(file)
     self.class.process optimize: [750, 562]
     @directory = 'stores/750x562/'
     store!(file)
  end

  def store_header(file)
    self.class.process resize_to_limit: [640, 640]
    @directory = 'headers/images/consumer_app_brand_logos/'
    store!(file)
  end

  def store_header_mailer(file)
    self.class.process resize_to_limit: [360, 120]
    @directory = 'headers/images/360x120/'
    store!(file)
  end

The above methods are executed in different jobs respectively, which means sometimes they are running concurrently.

Soon I noticed that some images resized by store_header_mailer were uploaded to the directory which should be owned by store_for_mobile, store_for_web and store_header. (e.g. 'headers/images/consumer_app_brand_logos/' got 120x120 images)

This issue didn't exist while I was using Resque.

I looked into carrierwave's source code, and noticed that

https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/uploader/processing.rb#L54

it is using class variables self.processors and class methods self.process while invoking image_magick command lines.

Is this part of code thread-safe? Thanks in advance.


Solution

  • Spent a few days to figure out what's going on. It is kinda an interesting case and also too long to explain it here so I decided to write a post for it, so for more details please read it there.


    Long story short is, carrierwave uses class variable to save processor method and dimensions, and it is not reset after the uploader finishes, thus the next uploader will be affected.