Symfony a máxima velocidad con ESI

Symfony a máxima velocidad con ESI

Desde que empezamos a crear componentes propios y paquetes (bundles en Symfony) para incorporar herramientas de gestión de contenidos en nuestros desarrollos, una de nuestras obsesiones ha sido el rendimiento.

La velocidad de carga de la web es básica para ofrecer una buena experiencia al usuario y para mantener su atención. Si una página tarda demasiado en cargarse, es muy probable que el usuario se sienta frustrado y termine abandonándola. Además, la velocidad de carga también es importante para el posicionamiento en los motores de búsqueda, ya que Google y otros buscadores premian a las páginas que cargan más rápido.

Pero también queremos cada vez experiencias más completas y complejas, más contenido en nuestras páginas, más funcionalidad. Mezclar ambas cosas implica optimizar el código, comprimir las imágenes, reducir la cantidad de recursos externos que se utilizan, entre otras técnicas.

Aparte de optimizar el código, las imágenes, reducir recursos, entre otras técnicas, hay muchas herramientas de software para cachear páginas. Cuando visitas una página web, el navegador descarga todos los recursos necesarios para mostrar esa página, como las imágenes, los estilos, los scripts, etc. Cada vez que vuelves a visitar esa página, el navegador tiene que volver a descargar todo el contenido, lo que puede ser muy lento.

El caché es como una memoria temporal que se guarda en el navegador o en el servidor, que permite guardar una copia de la página o de algunos elementos de la página en tu ordenador o dispositivo móvil. De esta manera, cuando vuelves a visitar esa página, el navegador puede cargar la página mucho más rápido porque ya tiene una copia guardada en caché.

Con el tema de la caché siempre surge el mismo dilema, ¿qué pasa cuando en una página todo sería cacheable menos un pequeño trocito? El estándar de HTML incorpora una tecnología, ESI, que viene de Edge Side Includes, y que sirve justo para esto. ESI es un lenguaje para incluir fragmentos de páginas web en otras páginas web. Es como una sentencia HTML include que funciona a través de HTTP.

En la mayoría de los sitios web se comparte mucho contenido entre páginas. Regenerar este contenido para cada vista de página es un desperdicio y ESI trata de solucionar esto permitiéndole decidir la política de caché para cada fragmento individualmente.

En Symfony tenemos "HTTP Cache ESI" (incluido en Symfony 2.2 o posterior), que te permite cachear partes específicas de tus páginas web para hacer que se carguen más rápido.

El HTTP Cache ESI funciona creando "fragmentos" que pueden ser almacenados en caché independientemente del resto de la página. Luego, cuando se solicita la página, los fragmentos se ensamblan para mostrar la página completa.

No es que estemos reinventando la rueda, hace 10 años la gente de Basecamp ya hablaba del Russian doll caching, o caché de "muñecas rusas", https://signalvnoise.com/posts/3690-the-performance-impact-of-russian-doll-caching por la idea de ir cacheando por partes incrementales. De un listado cacheas hasta el elemento de la lista, luego el listado, luego el componente que muestra el listado, luego el bloque en la página, luego la página. Puede parecer contraproducente por la cantidad de cosas que estás guardando en caché y porque, en caso de tener que cambiar un elemento del listado, hay que invalidar todas esas cosas que habíamos estado guardando, pero luego para regenerarlas, en el caso del listado, como el resto de elementos seguirán igual, en realidad se hace muy rápido.

¿Cómo implementarlo en Symfony?

Pero, ¿cómo funciona todo esto? Bueno, en primer lugar, necesitas crear los fragmentos que quieres cachear. Esto se hace utilizando el comando render_esi(controller) en tu plantilla de Twig, donde controller será el controlador que devuelve ese fragmento separado que quieres poder cachear. A continuación, necesitas configurar el caché para que use ESI. Esto se hace incluyendo la directiva esi a tu configuración de caché:

Si usas Varnish se hace con la línea set beresp.do_esi = true; a tu archivo de configuración de Varnish.

Si usas caché de Symfony se hace incluyendo la línea esi: true al archivo de configuración de caché.

En ambos casos hay que asegurarse de que los fragmentos tengan las cabeceras adecuadas. En particular, debes incluir las cabeceras Cache-Control y Surrogate-Control.

Una vez que hayas hecho esto, Symfony se encargará de almacenar en caché los fragmentos y de ensamblarlos cuando se solicite la página completa. ¡Y listo! Tu  web ahora será más rápida y eficiente gracias al HTTP Cache ESI de Symfony.