ActiveStorage with multiple storage services and multiple environments issue

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

It seems it’s already been discussed elsewhere

https://github.com/rails/rails/issues/42186