Hi guys,
since Rails 6.1 supports multiple storage services (which is great), I faced an issue I never had before. Let me explain.
Context
My storage.yml
contains many services
- local (test and dev)
- amazon_dev (for staging and sometimes dev)
- amazon_prod
Amazon services are S3 buckets to store images. Of course, based on the running environment, my Rails app will set one of those in the environment config files
- config.active_storage.service = :local (
development.rb
) - config.active_storage.service = :amazon_dev (
staging.rb
) - config.active_storage.service = :amazon_prod (
production.rb
)
Issue
What I am doing since Rails 5.0, as many developers, is to export production database and import it to staging and/or development database to fix bugs, do unit tests or add new features with an updated context.
Before Rails 6.1, everything was fine with ActiveStorage. But since it added a new column service_name
in table active_storage_blobs
, it imported the service name amazon_prod
in my development database on every records. And then the problem begins. When I delete an image on my local machine, it deletes it in real life in my amazon_prod
service because it has this service set in my storage.yml
and use it to delete the image in my prod S3 bucket.
Doing a dynamic storage.yml
(for example by removing amazon_prod
service when it’s not prod) will throw an error because it will say that it can’t find amazon_prod
service config and make the app unusable.
So even if my development.rb
is set to config.active_storage.service = :local
, it will use the service set in table active_storage_blobs
. Of course, when I upload a new picture now it’s using local
service.
Monkey Patch
what I am doing for now is a monkey patch, but I fear it’s not a good idea in long term:
# /config/initializers/active_storage.rb
Rails.application.config.after_initialize do
# prevent ActiveStorage storage.yml multiple services to delete production blobs from other environments
unless Rails.env.production?
ActiveStorage::Blob.class_eval do
def purge
return if self.service_name == "amazon_prod" # ignore purge if real prod service
super # otherwise, delete because it's staging or development blob
end
end
end
end
Conclusion
Any idea? Did I miss something in documentation? Other better idea or should I open an issue on Rails Github?
Also, I don’t think having a SQL script to rename all service_name
records when exporting prod to other environments is also a good idea.
Thanks