Ejecutar rake assets:precompile sin base de datos

Noté que la tarea rake assets:precompile requiere una conexión a la base de datos: esto significa que la tarea solo puede ejecutarse en tiempo de ejecución (después del despliegue) y no en tiempo de compilación (mientras se construye la imagen del contenedor). Dado que la tarea es muy costosa en términos de tiempo de ejecución, esto puede resultar molesto.

Al no ser desarrollador de Ruby/Rails, realicé algunas investigaciones y descubrí que este comportamiento podía desactivarse hasta Rails 4, y posteriormente los desarrolladores han recurrido a algunos trucos (conexión nula a la base de datos). Por supuesto, esto último requiere un conocimiento profundo de la aplicación para evitar romper algo.

Mientras buscaba una solución mejor, encontré este commit, que parece tener un espíritu similar. Por lo tanto, mis preguntas son:

  • ¿Los desarrolladores ya están trabajando en este asunto o hay razones técnicas por las que esto no se puede hacer?
  • Si algunas partes de la tarea realmente requieren la conexión a la base de datos, ¿sería factible dividir la tarea en dos (o más), de modo que parte del trabajo (por ejemplo, compilar localizaciones, minificar JS y CSS) se pueda realizar en tiempo de compilación?
  • ¿Existe algún workaround conocido en este momento (por ejemplo, “base de datos nula” como se mencionó anteriormente)?

Almacenamos los temas en la base de datos (se editan en la interfaz de administración), por lo que el CSS está dentro de PostgreSQL. Necesitas la conexión a la base de datos en el momento de la compilación para poder precompilarlos.

¡Gracias por la información!

¿Sería factible implementar la compilación de los idiomas por separado (para ejecutarla en el momento de la compilación)?
Además, puedo imaginar que algunos entornos (en mi caso, al menos) podrían no querer permitir cambiar los temas: ¿sería posible proporcionar un almacenamiento alternativo para el CSS en este caso?

Discutimos la idea de un interruptor que deshabilitara toda la interfaz de personalización, permitiendo que los archivos CSS se compilaran en tiempo de construcción y se cargaran en el almacenamiento de objetos durante la compilación (es decir, lo mismo que el núcleo de JS / los complementos).

Sin embargo, ese es un caso muy específico, que solo atraería a implementaciones de tipo empresarial, sin aportar ningún valor al 99% de las comunidades en la web. Por lo tanto, eso no está en nuestra hoja de ruta y es bastante difícil justificar dedicar trabajo a esto en lugar de desarrollar nuevas funciones o mejoras de rendimiento.

¿Podrías contarnos más sobre tu entorno y caso de uso?

Es bueno saber que la idea ya ha sido discutida. Entiendo que la cantidad de trabajo necesaria para el cambio probablemente sea demasiado grande para ser justificable.

En mi caso, Discourse se asociará a un sitio web preexistente, por lo que habrá un tema personalizado fijo para que coincida con el del sitio web: no tendría sentido cambiarlo dinámicamente.

Ah, me refería al caso de uso en el que no puedes conectarte a la base de datos en el “tiempo de compilación”.

Ah, bien: cuando construyo la imagen, trabajo en mi portátil de desarrollo. Luego, la imagen se sube a un repositorio y el sistema final (un VPS en DigitalOcean) la descarga desde allí.

La base de datos reside en un volumen del VPS, por lo que no se puede actualizar desde mi portátil: eso requeriría detener Discourse, sincronizar la base de datos con mi portátil mediante rsync, construir y subir la imagen, y finalmente reiniciar Discourse…

¿Así que estás ejecutando la base de datos y la aplicación dentro de un mismo droplet?

En ese caso, seguir nuestra guía de instalación oficial, que resulta en un droplet con la aplicación y la base de datos en el mismo droplet, te proporcionará un sitio completamente funcional, que puede actualizarse desde la interfaz web y, opcionalmente, desde la línea de comandos con una reconstrucción completa de la imagen.

Si con esto te refieres a “directamente en el host”, entonces no. Se están ejecutando en un contenedor, específicamente en un contenedor de podman. Idealmente, dividiría el contenedor en varios (uno para Discourse, otro para PostgreSQL, otro para Redis…), pero esto se relaciona con el problema que estamos discutiendo, por lo que aún no estoy seguro de cuál es la medida adecuada a tomar.

Me parece inseguro. Por lo general, pruebo los contenedores en mi entorno de desarrollo antes de implementarlos en producción. Además, idealmente los contenedores deberían ser de solo lectura.

Puedes dividir esos contenedores y luego ejecutar el proceso de arranque de la imagen en otra droplet de corta duración. Dado que las droplets se facturan por hora, esto será económico. Incluso puedes aprovechar la red privada de las droplets entre el host del contenedor de la base de datos y el host del contenedor de “compilación”.

Jeje, gracias por la idea, pero eso empieza a complicarse. Además, no resolverá el problema, porque aún tendremos que detener Discourse y esperar a que finalice el proceso de arranque, o podrían aparecer datos inconsistentes.

Parece que tendremos que conformarnos con un tiempo de inactividad prolongado (de 5 a 6 minutos para la migración y la precompilación) durante las actualizaciones. Aun así, agradecería que mantuvieran un issue de baja prioridad en el rastreador, quizás con un enlace a este tema.

¡Gracias y sigan con el buen trabajo!

No, ese no es el caso.

Debería ser solo “unos segundos” en lugar de 5-6 minutos, pero necesitas contenedores de datos y web dedicados. Depende de lo que quieras priorizar.

Además, según las pruebas de rendimiento, la reconstrucción en un servidor rápido debería tomar unos 3 minutos.

Bueno, gracias. En este caso, definitivamente separaré los contenedores, lo cual es una arquitectura mejor de todos modos.

Sin embargo, no logro ver cómo esto marca la diferencia. Si no estoy equivocado, todos los contenedores compartirán todas las CPUs del host (a menos que se configure de otra manera), por lo que los procesos deberían ejecutarse en paralelo en ambos casos. ¿Estoy pasando por alto algo?

Tu contenedor antiguo seguirá ejecutándose mientras el nuevo se está inicializando. Luego, puedes apagar rápidamente el antiguo y comenzar el nuevo, de modo que el tiempo de inactividad será menor.