Migrar ActiveStorage de Disk a otro servicio
Cuando empiezo una aplicación nueva de Rails intento configurar lo menos posible, especialmente cuando se trata de servicios externos. Cuando ya se hace insostenible es cuando me decido a dar el paso. En este caso tengo una aplicación corriendo en una Raspberry, a la que accede mi familia desde diferentes partes del mundo. Es una pequeña red social privada. Ha llegado el momento en el que he sentido que lo mejor es mover las imágenes y vídeos a un proveedor externo, para mejorar la latencia y el cach
Lo primero de todo es configurar un servicio y usarlo como mirror para ver que todo funciona correctamente.
Tras configurarlo, la mejor manera de comprobar que funciona es subir algún fichero desde la aplicación e ir al servicio configurado y ver que efectivamente se han creado ficheros en él.
El servicio de mirroring solo funciona desde el momento en que se configura, no tiene carácter retroactivo. Tendremos que, por lo tanto, migrar todos los archivos antiguos a mano.
La opción más efectiva que he encontrado consiste en lo siguiente:
# Fetch services
local_service = ActiveStorage::Blob.services.fetch(:local)
cloudflare_service = ActiveStorage::Blob.services.fetch(:cloudflare)
# Loop through all blobs, skipping already migrated ones
ActiveStorage::Blob.find_each(batch_size: 50) do |blob|
next if blob.service_name == "cloudflare"
begin
# Download from local disk and upload to Cloudflare
local_service.download(blob.key) do |file|
cloudflare_service.upload(blob.key, file, checksum: blob.checksum)
end
# Update blob to point to Cloudflare
blob.update!(service_name: "cloudflare")
puts "Migrated blob #{blob.id}"
rescue => e
puts "Failed to migrate blob #{blob.id}: #{e.message}"
end
end
Aunque intento llegar a soluciones por mi mismo, en este caso me estaba complicando la vida y he acabado tirando de una IA gratuita para que me revise el código. Resulta que ActiveStorage::Blob tiene métodos download y upload que vienen finísimos para esta tarea.
Lo ideal es poner esto en una tarea de rake que encole jobs y bla bla bla, pero aquí hemos venido a jugar. Ha tardado un buen rato y al final todos los ficheros han acabado duplicados.
El siguiente paso es cambiar el mirror de tal forma que el servicio primario sea cloudflare (o el que tú quieras) y el secundario sea local. Y en cuanto hayas comprobado que todo funciona como esperas, puedes cargarte el mirror y funcionar solo con el nuevo servicio.
Lo que me falta por hacer ahora es borrar los ficheros del disco, pero no tengo prisa y no quiero cargarme nada, así que creo que se quedarán ahí hasta que sea inevitable mover ficha.
Si lo que deseas o necesitas es migrar entre servicios que no sean locales, el proceso es más fácil todavía, ya que puedes usar el método .mirror_later en cada blob. Puedes leer más sobre este método en el blog de Oliver Eidel.