Compartir

Uso óptimo de la caché en Magento 2. #Codehacks

10 abril, 2018

La caché es el mecanismo más efectivo del que disponemos para mejorar la performance de una página.

Generalmente hablando hay dos métodos de cachear contenido:

  • Server side
  • Client side

Proporcionan la posibilidad de reducir los recursos usados de los servidores y sirven el contenido al cliente de una manera mas rapida.

Básicamente lo que hacemos con la caché es almacenar la respuesta de una pagina solicitada por un cliente para que la próxima vez que alguien nos la pida se la mostremos más rápido.

La librería de caché de Magento contiene un simple proxy inverso de PHP que habilita el almacenamiento en caché de la página (FPC). Un proxy inverso actúa como un intermediario entre los visitantes y su aplicación y puede reducir la carga en su servidor.

No obstante Magento recomienda el uso de Varnish para entornos en producción, el cual puede almacenar los archivos de caché en los siguientes sistemas:

  • File system (No es necesario hacer nada para usar el almacenamiento en caché basado en archivos)
  • Base de datos
  • Redis

Contenido cacheable y no cacheable

Los términos cacheable y no cacheable se usan para definir cuando una página tiene que ser almacenada en caché y cuando no.

Ejemplos de páginas cacheables:

  1. Listado de productos
  2. CMS
  3. Fichas de producto

Ejemplos de páginas no cacheables:

  1. Cuenta de usuario
  2. Carrito
  3. Checkout

Contenido público y privado

Los proxies inversos sirven contenido “público” o compartido a más de un usuario.

Sin embargo, la mayoría de los sitios web de Magento generan contenido “privado” dinámico y personalizado que solo debe ser servido a un usuario, lo que presenta desafíos únicos de almacenamiento en caché. Para abordar estos desafíos, Magento puede distinguir entre dos tipos de contenido:

  1. Público: En el lado del servidor es donde almacenaremos la información pública de nuestras tiendas (La que es igual para todas las visitas)
  2. Privado: En el lado de cliente (navegador) es donde almacenamos la información privada (Datos de clientes, carrito, lista de deseos…)

Contenido público

Teniendo en cuenta que a nivel de servidor es desde donde gestionamos la caché de contenido público, qué podemos hacer para gestionar las variaciones de páginas o cómo podemos mostrar contenido diferente en función de grupos de clientes, segmentos de usuarios…

Desactivar la caché en una página

Para hacer que una página sea no cacheable basta con que uno de sus bloques tenga la siguiente directiva en el xml cacheable=”false”. Especial cuidado con esta directiva, por cuestiones evidentes.

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!--
~ @author Interactiv4 Team
~ @copyright  Copyright © Interactiv4 (https://www.interactiv4.com)
-->

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
   <referenceContainer name="content">

       <block class="Vendor\Namespace\Block\Index" name="fpc_index_index" template="Vendor_Namespace::index.phtml" cacheable="false"/>

   </referenceContainer>
</page>

También se puede desactivar la caché de una página añadiendo parámetros a la cabecera de una petición desde un controlador de magento (Varnish only):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
namespace Interactiv4\FPC\Controller\Index;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;

class Index extends Action
{
   protected $pageFactory;

   public function __construct(
       Context $context,
       PageFactory $pageFactory
   ) {
       $this->pageFactory = $pageFactory;
       return parent::__construct($context);
   }

   public function execute()
   {
       $page = $this->pageFactory->create();
       //We are using HTTP headers to control various page caches (varnish, fastly, built-in php cache)
       $page->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0', true);
       return $page;
   }
}

Configurar variaciones de página

Muchos proxys inversos utilizan la URL como clave para los registros en caché url pero en Magento las URL no son los suficientemente únicas para permitirlo, lo cual puede provocar problemas de:

  • Colisión en el almacenamiento de la caché.
  • Fugas de información no deseadas (por ejemplo, sitio web en francés parcialmente visible en un sitio web en inglés, precios para grupos de clientes visibles en público, etc.)

Para salvar ese problema se utilizan las variables de contexto HTTP. Las variables de contexto permiten que la aplicación Magento sirva contenido diferente en la misma URL en función de:

  • Grupo de clientes
  • Idioma seleccionado
  • Tienda seleccionada
  • Moneda seleccionada
  • Si un cliente está conectado o no.

Las variables de contexto no deberían ser específicas para usuarios individuales porque las variables se usan en claves de caché para contenido público. En otras palabras, una variable de contexto por usuario da como resultado una copia separada del contenido en caché en el servidor para cada usuario.

Magento genera un hash basado en todas las variables de contexto

\Magento\Framework\App\Http\Context::getVaryString

El hash y la URL actual se utilizan como claves para el almacenamiento en caché.

Esta función lo que nos permite es generar la cookie X-Magento-Vary que es transferida a la capa HTTP. Los proxies HTTP se pueden configurar para calcular un identificador único para el caché basado en la cookie y URL.

https://github.com/magento/magento2/blob/2.0/app/code/Magento/PageCache/etc/varnish4.vcl#L63-L68

Lo que podemos ver en ese vcl de ejemplo, proporcionado por magento, es que si la capa de HTTP recibe la cookie X-Magento-Vary, tendrá en cuenta el contenido de esta para la generación del hash para esa página.

\Interactiv4\FPC\Plugin\CustomerGenderContextPlugin.php

Cuál sería un uso de esta funcionalidad, por ejemplo en el proyecto de Dufry podríamos establecer una variación de la caché de la página en función de las edades de los clientes, para mostrar productos con alcohol o sin alcohol.

Invalidar contenido público

Se puede borrar el contenido en caché inmediatamente después de que una entidad cambie. Magento usa IdentityInterface para vincular entidades en la aplicación con contenido cacheado y para saber qué caché borrar cuando cambia una entidad.
Para hacer que nuestros módulos utilicen este sistema tenemos que hacer lo siguiente:

  1. El modelo que gestione nuestra entidad tiene que implementar la siguiente interfaz.
  2. Magento/Framework/DataObject/IdentityInterface.php

  3. El bloque que muestre contenido de nuestra entidad y que utilice el modelo anterior también tiene que implementar la anterior interfaz

Podemos utilizar la entidad de categoría para ver cómo funciona. Si nos vamos al modelo de categoría vemos que este hace uso de la función getIdentities:

/Magento/Catalog/Model/Category.php

Y este método es referenciado en el bloque que pinta la categoría.

/Magento/Catalog/Block/Category/View.php

Magento usa etiquetas de caché para la creación de enlaces. El rendimiento del almacenamiento en caché depende directamente del número de etiquetas por registro de caché, por lo que debe intentar minimizar el número de etiquetas y utilizarlas solo para las entidades que se utilizan en el modo de producción. En otras palabras, no use la invalidación para acciones relacionadas con la configuración de la tienda.

¿Pero al final que es lo que hacen? Estos métodos son utilizados por Magento para establecer la cookie X-Magento-Tags en la cabecera de cada petición.

Esta cabecera es utilizada en el proceso que genera y almacena la caché. Podemos ver el proceso en el siguiente fichero:

Magento/Framework/App/PageCache/Kernel.php::process()

Importante a tener en cuenta es que las Identities son para la FPC. Para la caché de los bloques deberemos seguir implementando los cache_tags y cache_lifetime.

Si nos fijamos en la siguiente clase, vemos que Varnish tambien tiene en cuenta dicha cookie a la hora de generar los Bloques ESI.

Magento\PageCache\Controller\Block\Esi.php

A parte de que implementemos esta interfaz en nuestros módulos también tenemos que tener en cuenta en qué podemos hacer uso de plugins para modificar la generación de los Tags de los bloques de magento con la finalidad de añadir lógica a la invalidación de cachés.

Lista de verificación de página cacheable

  • Las páginas usan peticiones GET
  • Las páginas solo renderizan bloques cacheables
  • Las páginas se procesan sin datos privados confidenciales.
  • Las funcionalidades específicas tanto para la sesión actual (cliente) como para la página debe escribirse usando JavaScript (por ejemplo, la lista de productos relacionados debe excluir los elementos que ya están en el carrito de compras)
  • Los modelos y los bloques tienen que identificarse correctamente para el soporte de invalidación
  • Crearemos variables de contexto si queremos mostrar información contenido público diferente usando la misma URL

Lista de verificación de página no almacenable en caché

  • Usar peticiones POST para modificar el estado de Magento (por ejemplo, agregando al carrito de compras, a la lista de deseos, etc.)
  • Los bloques que no pueden almacenarse en caché deben marcarse como no cacheables en el diseño. Sin embargo, tenga en cuenta que agregar un bloque no almacenable en caché a una página impide que la memoria caché de página completa guarde en caché esa página.
  • Los controladores que no usan diseños deben establecer encabezados HTTP sin caché

Contenido privado

Como el contenido privado es específico para cada usuario, tiene sentido que éste sea gestionado por el client side o navegador. Magento utiliza la librería de javascript customer-data para almacenar el contenido de los usuarios en el local storage del navegador, invalidar datos privados utilizando reglas personalizables y sincronizar datos con el servidor.

Magento/Customer/view/frontend/web/js/customer-data.js

Crear una fuente de contenido privado

Si queremos mostrar contenido privado de los usuarios en alguno de nuestros módulos deberemos crear una nueva “Section source”. Magento recomienda usar la siguiente nomenclatura a modo de estandarizar todos los Customer Source Section.

Vendor/ModuleName/CustomerData

Las clases que creemos para mostrar el contenido del usuario deberán implementar la siguiente interface y hacer uso del método getSectionData

Magento\Customer\CustomerData\SectionSourceInterface

Tendremos que definir dicho Data source en nuestro fichero di.xml

magento/module-catalog/etc/frontend/di.xml::46

Esa configuración enlaza al fichero DataSource que se utilizara en nuestro template.

\Magento\Catalog\CustomerData\CompareProduct

Renderizar contenido privado

Una vez que hemos definido el data Source necesitamos un bloque y un template para mostrar el contenido privado. Este contenido privado sera renderizado mediante el Ui component.

El contenido privado se reemplazará usando placeholders en los bloques (usando la sintaxis Knockout). Para hacerle saber a Knockout el origen de los datos privados que tiene que insertar en nuestra plantilla tendremos que especificar el siguiente elemento data-bind = “scope: ‘compareProducts'”, que marca la vinculación con nuestro compareProducts declarado en el fichero di.xml, en el html.

module-catalog/view/frontend/templates/product/compare/sidebar.phtml

Desde este phtml podemos ver como se hacen llamadas a los datos del DataSource definido en compareProducts.

1
<a data-bind="attr: {'href': compareProducts().listUrl}"

Para poder usar el ui component que renderiza la información del bloque deberemos inicializarlo mediante la el método _super()

magento/module-catalog/view/frontend/web/js/view/compare-products.js

Invalidar contenido privado

Para poder invalidar la caché de los bloques que generan contenido privado deberemos configurar el fichero sections.xml

magento/module-catalog/etc/frontend/sections.xml

Este fichero define una serie de peticiones POST y PUT que invalidan la caché del bloque que utiliza el DataSource compareProducts

(No te pierdas nuestros post #Codehacks)

Categoría
Etiquetas
Autor

Compartir

Suscríbete a nuestra newsletter

También te puede interesar

Usamos cookies de terceros para mejorar nuestros servicios y obtener datos estadísticos de tus hábitos de navegación. Si continúas navegando consideramos que aceptas su uso. Puedes obtener más información en Política de privacidad y cookies