Search code examples
ruby-on-railsrails-activestorage

Activestorage - transfer all assets from one bucket to another bucket


Im switching my app from Vultr to DigitalOcean. Right now I have a bucket configured on Vultr along with the former server. When I try to access my activestorage images on Vultr from DigitalOcean the images load only 10% of the time and most requests result in 502 errors.

Since Im completely moving this app away from Vultr I feel like it would be a good idea to transfer my app's image assets over to a DigitalOcean bucket.

I've found a lot of posts and a couple of blogs with migration scripts but they're focused on migrating from local to bucket. I havnt found anything on moving from one bucket to another.

I have no idea how to do this, has anyone ever moved from one bucket to another? If so, how did you do it?


Solution

  • We also wanted to do an online, incremental migration from one ActiveStorage backend to another, this is some of the extracted code that handled it for us.

    It iterates through each blob, copying the file and updating the blob to reference the new service. It leaves the originals intact in case you need to toggle back in case of a problem.

    We didn't bother copying any of the variants, instead opting to just let them regenerate as needed, but the code to copy them would probably be similar.

    source_service = ActiveStorage::Blob.services.fetch(:service_a)
    destination_service = ActiveStorage::Blob.services.fetch(:service_b)
    # :service_a/b above should be top-level keys from `config/storage.yml`
    
    ActiveStorage::Blob.where(service_name: source_service.name).find_each do |blob|
      key = blob.key
    
      raise "I can't find blob #{blob.id} (#{key})" unless source_service.exist?(key)
    
      unless destination_service.exist?(key)
        source_service.open(blob.key, checksum: blob.checksum) do |file|
          destination_service.upload(blob.key, file, checksum: blob.checksum)
        end
      end
      blob.update_columns(service_name: destination_service.name)
    end