Testing con caché en CircleCI

DevOps may. 10, 2021

En Platanus siempre hemos tenido tests que se corren automáticamente en CircleCI. Esto nos permite revisar que las aplicaciones funcionen correctamente cada vez que alguien sube código nuevo.

Si bien el flujo funciona super bien -más de algún bug grave se ha evitado porque lo capturamos en los tests- a veces es lento. Una corrida de todos los tests de una aplicación depende de cuántos haya, pero también de todo el proceso de levantar el entorno necesario para correr los tests. Y ese levantamiento puede ser lentísimo. En este post les contaré como logramos acelerarlo notablemente.

Los builds y su tiempo

Los servicios de integración continua proveen máquinas virtuales donde los programadores instalan sus proyectos, realizan lo que quieren hacer (en nuestro caso correr los tests), y las desechan. Esto implica que cada vez que se quiere hacer algo hay que instalar el proyecto de nuevo. Para eso hay que tener un archivo de configuración con los pasos.

En Platanus creamos nuestros proyectos con potassium, una herramienta opensource para generar proyectos con toda la configuración inicial que nos gusta usar. Dentro de esa configuración están esos pasos necesarios para CI.

Nuestro build para los tests se compone principalmente de:

  • Indicarle a CI qué imágenes de Docker vamos a usar
  • Esperar que los servicios (como la base de datos) estén listos
  • Instalar las dependencias de Ruby con Bundler
  • Instalar las dependencias de Javascript con Yarn
  • Configurar la base de datos con nuestro esquema de datos
  • Correr los tests

Si se fijan son varios pasos anteriores a correr los tests. Yo los llamo el costo fijo, varían poco entre proyecto y proyecto y da lo mismo si tengo 2 tests o 2000, la configuración inicial estará igual. En un proyecto cualquiera el setup puede tomar más de 5 minutos y correr los tests menos de 30 segundos.

Los pasos de tests Run rspec y Run jest toman 7 segundos! El resto toma más de 4 minutos y medio.

El sueño del pibe

Me encantaría que cada vez que subo código a GitHub y se corren los tests todo demorara 7 segundos. Si tan solo me pudiera saltar la primera parte...

Aquí es donde viene el caché al rescate. CircleCI (y otros servicios de CI) nos permiten guardar ciertas cosas de nuestro build para reutilizarlas. En particular recomiendan guardar las dependencias y utilizarlas después. Uno tiene que especificar qué rutas y carpetas guardar, cómo llamarlas y cuándo ir a buscarlas.

Si bien hay partes que no nos vamos a ahorrar, las dependencias parecen ser lo más lento, entonces ahorrarnos esos pasos sería un alivio gigante.

El panorama general con caché se ve más o menos así:

  • Indicarle a CI qué imágenes de Docker vamos a usar
  • Esperar que los servicios (como la base de datos) estén listos
  • Revisar si hay algo en el caché tanto para Ruby como para Javascript
  • Instalar las dependencias de Ruby con Bundler (por si hay cambios)
  • Instalar las dependencias de Javascript con Yarn (por si hay cambios)
  • Guardar el caché de ser necesario
  • Configurar la base de datos con nuestro esquema de datos
  • Correr los tests

Si bien agregamos 2 pasos nuevos, ahora nos podemos ahorrar los lentos pasos de instalación de dependencias.

En 9 segundos ya tenemos restaurado el caché!

Ahora que la parte de dependencias toma menos de 20 segundos, el build entero en CircleCI toma menos de un minuto.

Show me the money code

Para terminar los voy a dejar con un ejemplo de código del archivo de configuración. No les aseguro que si lo copian y lo pegan les va a funcionar directamente porque está pensado para nuestro stack (Ruby on Rails + Vue, tests en RSpec + Jest), pero les puede servir como base para agregar caché y empezar a ahorrar tiempo en sus builds de CI.

El código va en la ruta .circleci/config.yml en cada repositorio. Si bien tiene hartos pasos, se resumen en el punteo que hicimos antes.

Desde que implementamos esto en algunos proyectos la carga en CircleCI bajó drásticamente. Somos un equipo muy activo y en crecimiento por lo que la cantidad de pull requests al día es altísima. Cada uno de ellos implica que se corran los tests, por lo que bajar de ~5 minutos a ~1 es notable, sobretodo cuando eso implica poder publicar los cambios para mejorar nuestros productos más rápido.

I see this as an absolute win
¡Genial! Te has suscrito con éxito.
¡Genial! Ahora, completa el checkout para tener acceso completo.
¡Bienvenido de nuevo! Has iniciado sesión con éxito.
Éxito! Su cuenta está totalmente activada, ahora tienes acceso a todo el contenido.