I'm using Sidekiq and Carrierwave to upload images to S3. The background job does the following things:
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
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.
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.