ActiveStorage currently allows variants to be created on demand (by default) or in the background (using preprocessed). This can be problematic for high-traffic applications. For instance, immediately after an attachment is created, thousands of requests could be made for a single variant, causing both the controller and background jobs to race in creating the same variant.
To give more control over variant creation, we propose an immediate option to allow variants to be created simultaneously with the attachment:
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [4, 4], immediate: true
end
In this case, when the avatar is created, the same process will immediately create a variant ensuring that it is available without delay and only once process is responsible for the creation.
I’ve created a PR here. I’ve avoided changing or refactoring existing patterns but am open to discussion!
This will be the first of several PRs to assist others managing high-volume traffic of public assets.
If you haven’t already you might want to join the official rails discord which I think is used mostly for asking for PR reviews and is more actively monitored by the core team than GitHub PRs. (Take with a grain of salt). But also just wanted to throw my two cents out there…
I personally think a sane default would be utilizing a cache based concurrency control mechanism to check if the job exists. For example, adding the following to TransformJob
before_enqueue do |job|
if Rails.cache && Rails.application.config.example_config_flag
cache_key = [job.class.name, job.arguments.join("/")].join("/")
fail if Rails.cache.exist? cache_key
Rails.cache.write cache_key, true
end
end
after_perform do |job|
if Rails.cache
Rails.cache.delete [job.class.name, job.arguments.join("/")].join("/")
end
end
I think this is a more sane default because blocking writes to process those variants is probably a worse alternative for the majority of apps that don’t want the described job stampeding. But the default of job stampeding is also less than ideal, IMO.