Invalidaci贸n de cach茅 en cascada. Parte 1

Desde hace varios a帽os, como casi todos los art铆culos sobre enfoques avanzados de almacenamiento en cach茅 recomiendan el uso de las siguientes t茅cnicas en producci贸n:

  • Agregar al archivo nombres informaci贸n sobre la versi贸n de los datos contenidos en ellos (generalmente en forma de un hash de los datos en los archivos).
  • Configuraci贸n de encabezados HTTP Cache-Control: max-age y Expires , que controlan el tiempo de almacenamiento en cach茅 de los materiales (lo que elimina la revalidaci贸n de los materiales relevantes para los visitantes que regresan al recurso).



Todas las herramientas para construir proyectos que conozco admiten agregar al archivo hash los nombres de sus contenidos. Esto se hace usando una regla de configuraci贸n simple (como lo que se muestra a continuaci贸n):

 filename: '[name]-[contenthash].js' 

Tal apoyo generalizado para esta tecnolog铆a ha llevado al hecho de que esta pr谩ctica se ha vuelto extremadamente com煤n.

Los expertos en rendimiento de proyectos web tambi茅n recomiendan utilizar t茅cnicas de separaci贸n de c贸digo . Estas t茅cnicas permiten dividir el c贸digo JavaScript en paquetes separados. Dichos paquetes pueden ser descargados por el navegador en paralelo, o incluso solo cuando sean necesarios, a solicitud del navegador.

Una de las muchas ventajas de la separaci贸n de c贸digo, en particular, relacionada con las t茅cnicas avanzadas de almacenamiento en cach茅, es que los cambios realizados en un archivo fuente separado no invalidan la cach茅 de todo el paquete. En otras palabras, si se emiti贸 una actualizaci贸n de seguridad para el paquete npm creado por el desarrollador "X" y los desarrolladores fragmentan el contenido de node_modules , entonces solo el fragmento que contiene los paquetes creados por "X" tendr谩 que cambiarse.

El problema aqu铆 es que si todo esto se combina, esto rara vez conduce a un aumento en la eficiencia del almacenamiento en cach茅 de datos a largo plazo.

En la pr谩ctica, los cambios en uno de los archivos de c贸digo fuente casi siempre resultan en la invalidaci贸n de m谩s de un archivo de salida del sistema de ensamblaje de paquetes. Y esto se debe precisamente al hecho de que se han agregado hashes a los nombres de archivo que reflejan las versiones de los contenidos de estos archivos.

Problema de versiones del nombre de archivo


Imagine que ha creado y desplegado un sitio web. Ha utilizado la divisi贸n de c贸digo, como resultado, la mayor parte del c贸digo JavaScript de su sitio se carga a pedido.

En el siguiente diagrama de dependencia, puede ver el punto de entrada de la base del c贸digo: el fragmento ra铆z de main , as铆 como tres fragmentos de dependencia cargados de forma as铆ncrona: dep1 , dep2 y dep3 . Tambi茅n hay un fragmento de vendor que contiene todas las dependencias del sitio de node_modules . Todos los nombres de archivos, de acuerdo con las pautas de almacenamiento en cach茅, incluyen hashes del contenido de estos archivos.


脕rbol de dependencia de m贸dulo JavaScript t铆pico

Dado que los dep3 dep2 y dep3 importan m贸dulos del fragmento del vendor , en la parte superior de su c贸digo generado por el dep3 del proyecto, lo m谩s probable es que encontremos comandos de importaci贸n que se vean as铆:

 import {...} from '/vendor-5e6f.mjs'; 

Ahora pensemos en lo que suceder谩 si el contenido del fragmento del vendor cambia.

Si esto sucede, el hash en el nombre del archivo correspondiente tambi茅n cambiar谩. Y dado que el enlace al nombre de este archivo est谩 en los comandos de importaci贸n para dep3 dep2 y dep3 , entonces ser谩 necesario que estos comandos de importaci贸n cambien:

 -import {...} from '/vendor-5e6f.mjs'; +import {...} from '/vendor-d4a1.mjs'; 

Sin embargo, dado que estos comandos de importaci贸n son parte del contenido de los dep3 dep2 y dep3 , cambiarlos significa que el hash del contenido de los archivos dep2 y dep3 tambi茅n dep3 . Y eso significa que los nombres de estos archivos tambi茅n cambiar谩n.

Pero esto no termina ah铆. Como el fragmento main importa fragmentos dep2 y dep3 , y sus nombres de archivo han cambiado, los comandos de importaci贸n en main tambi茅n cambiar谩n:

 -import {...} from '/dep2-3c4d.mjs'; +import {...} from '/dep2-2be5.mjs'; -import {...} from '/dep3-d4e5.mjs'; +import {...} from '/dep3-3c6f.mjs'; 

Y finalmente, dado que el contenido del archivo main ha cambiado, el nombre de este archivo tambi茅n tendr谩 que cambiar.

As铆 es como se ver谩 el diagrama de dependencia.


M贸dulos en el 谩rbol de dependencia afectados por un solo cambio en el c贸digo de uno de los nodos hoja del 谩rbol

Este ejemplo muestra c贸mo un peque帽o cambio de c贸digo realizado en un solo archivo condujo a la invalidaci贸n de la cach茅 del 80% de los fragmentos del paquete.

Aunque es cierto que no todos los cambios tienen consecuencias tan tristes (por ejemplo, invalidar el cach茅 del nodo hoja conduce a invalidar el cach茅 de todos los nodos hasta la ra铆z, pero invalidar el cach茅 ra铆z no causa que la invalidaci贸n en cascada alcance la captura de la hoja), en un mundo ideal no tendr铆amos que lidiar con invalidaciones innecesarias de cach茅.

Esto nos lleva a la siguiente pregunta: "驴Es posible obtener los beneficios de los recursos inmutables y el almacenamiento en cach茅 a largo plazo, sin sufrir invalidaciones de cach茅 en cascada?"

Enfoques de resoluci贸n de problemas


El problema con los hashes del contenido de los archivos en los nombres de los archivos, desde un punto de vista t茅cnico, no es que los hashes est茅n en los nombres. Se encuentra en el hecho de que estos hashes aparecen dentro de otros archivos. Como resultado, la memoria cach茅 de estos archivos se deshabilita al cambiar los valores hash en los nombres de los archivos de los que dependen.

La soluci贸n a este problema es utilizar el lenguaje del ejemplo anterior para hacer posible importar el fragmento de vendor por los fragmentos dep2 y dep3 sin especificar la informaci贸n de versi贸n del archivo de fragmentos de vendor . Al hacerlo, debe asegurarse de que la versi贸n del vendor descargada sea correcta, teniendo en cuenta las versiones actuales de dep2 y dep3 .

Al final result贸 que, hay varias formas de lograr este objetivo:

  • Importar tarjetas.
  • Trabajadores de servicio.
  • Scripts nativos para cargar recursos.

Considere estos mecanismos.

Enfoque n. 掳 1: Importar tarjetas


Los mapas de importaci贸n son la soluci贸n m谩s simple para la invalidaci贸n de cach茅 en cascada. Adem谩s, este mecanismo es m谩s f谩cil de implementar. Pero, desafortunadamente, solo es compatible con Chrome (esta funci贸n, adem谩s, debe habilitarse expl铆citamente).

A pesar de esto, quiero comenzar con la historia sobre las tarjetas de importaci贸n, porque estoy seguro de que esta decisi贸n se convertir谩 en la m谩s com煤n en el futuro. Adem谩s, la descripci贸n de trabajar con tarjetas de importaci贸n ayudar谩 a explicar las caracter铆sticas de otros enfoques para resolver nuestro problema.

El uso de mapas de importaci贸n para evitar la invalidaci贸n de cach茅 en cascada consta de tres pasos.

鈻峆aso 1


Debe configurar el paquete para que al compilar el proyecto no incluya hashes del contenido de los archivos en sus nombres.

Si ensambla un proyecto cuyos m贸dulos se muestran en el diagrama del ejemplo anterior, sin incluir hashes de su contenido en los nombres de archivo, los archivos en el directorio de salida del proyecto se ver谩n as铆:

 dep1.mjs dep2.mjs dep3.mjs main.mjs vendor.mjs 

Los comandos de importaci贸n en los m贸dulos correspondientes tampoco incluir谩n hashes:

 import {...} from '/vendor.mjs'; 

鈻峆aso 2


Debe usar una herramienta como rev-hash y usarla para generar una copia de cada archivo con un hash agregado a su nombre que indique la versi贸n de su contenido.
Despu茅s de que esta parte del trabajo se haya completado, el contenido del directorio de salida deber铆a parecerse al que se muestra a continuaci贸n (tenga en cuenta que ahora hay dos opciones para cada archivo):

 dep1-b2c3.mjs", dep1.mjs dep2-3c4d.mjs", dep2.mjs dep3-d4e5.mjs", dep3.mjs main-1a2b.mjs", main.mjs vendor-5e6f.mjs", vendor.mjs 

鈻峆aso 3


Debe crear un objeto JSON que almacene informaci贸n sobre la correspondencia de cada archivo en cuyo nombre no hay hash para cada archivo en cuyo nombre hay un hash. Este objeto debe agregarse a las plantillas HTML.

Este objeto JSON es un mapa de importaci贸n. As铆 es como podr铆a verse:

 <script type="importmap"> {  "imports": {    "/main.mjs": "/main-1a2b.mjs",    "/dep1.mjs": "/dep1-b2c3.mjs",    "/dep2.mjs": "/dep2-3c4d.mjs",    "/dep3.mjs": "/dep3-d4e5.mjs",    "/vendor.mjs": "/vendor-5e6f.mjs",  } } </script> 

Despu茅s de eso, cada vez que el navegador ve el comando de importaci贸n del archivo ubicado en la direcci贸n correspondiente a una de las claves del mapa de importaci贸n, el navegador importa el archivo que coincide con el valor de la clave.

Si utiliza este mapa de importaci贸n como ejemplo, puede descubrir que el comando de importaci贸n que se refiere al archivo /vendor.mjs realmente consultar谩 y cargar谩 el archivo /vendor-5e6f.mjs :

 //    `/vendor.mjs`,  `/vendor-5e6f.mjs`. import {...} from '/vendor.mjs'; 

Esto significa que el c贸digo fuente de los m贸dulos puede referirse f谩cilmente a los nombres de los m贸dulos que no contienen hashes, y el navegador siempre descargar谩 los archivos cuyos nombres contienen informaci贸n sobre las versiones de sus contenidos. Y, dado que no hay hashes en el c贸digo fuente de los m贸dulos (solo est谩n presentes en el mapa de importaci贸n), los cambios en estos hashes no conducir谩n a la invalidaci贸n de m贸dulos que no sean aquellos cuyo contenido realmente ha cambiado.

Quiz谩s ahora se pregunte por qu茅 cre茅 una copia de cada archivo en lugar de simplemente renombrarlos. Esto es necesario para admitir navegadores que no pueden funcionar con mapas de importaci贸n. En el ejemplo anterior, dichos navegadores solo ver谩n el archivo /vendor.mjs y simplemente descargar谩n este archivo, haciendo lo que suelen hacer, y encontrar谩n construcciones similares. Como resultado, resulta que ambos archivos deben existir en el servidor.

Si desea ver los mapas de importaci贸n en acci贸n, aqu铆 hay un conjunto de ejemplos que demuestran todas las formas de resolver el problema de invalidaci贸n de cach茅 en cascada que se muestra en este art铆culo. Adem谩s, eche un vistazo a la configuraci贸n del ensamblaje del proyecto , en caso de que est茅 interesado en aprender c贸mo gener茅 el mapa de importaci贸n y los hashes de versi贸n para cada archivo.

Continuar谩 ...

Estimados lectores! 驴Eres consciente de la invalidaci贸n de cach茅 en cascada?


Source: https://habr.com/ru/post/472860/


All Articles