[🛠] Reducir el tiempo de build del blog multilingüe de 21 minutos a menos de 2
✨ Resumen de GPT-5.5
Registro de cómo rastreé con profile un build de Jekyll que superaba los 20 minutos tras la expansión multilingüe, eliminé renderizados repetidos y escaneos de todo el sitio, y lo bajé a 1 minuto y 50 segundos.
En el post anterior sobre la introducción multilingüe, añadí al blog una estructura operativa multilingüe.
Al principio, me sentí muy orgulloso de ello.
ko, en, ja, zh-Hans, es, pt-BR, fr, id.
Había mensajes, menús,hreflang, y conteos de vistas compartidas. Desde el exterior, se había convertido en un blog multilingüe bastante plausible.
Luego surgió otro problema de inmediato.
La construcción estaba tardando demasiado.
La primera producción se veía así.
done in 1311.791 seconds
21 minutos y 52 segundos.
Ese no es un número que debería salir de un blog simple. Si editar un post hace que el build tarde más de 20 minutos, con el tiempo acabaré pasando más tiempo esperando builds que escribiendo.
Al principio, fue tentador pensar: “Bueno, ahora hay ocho idiomas, así que por supuesto se volvió más lento”.
Pero esa fue una conclusión demasiado rápida.
El conteo de páginas aumentó. Aun así, alcanzar el rango de 20 minutos significaba que algo más estaba pasando. Así que esta vez, en lugar de arreglar por el tacto, encendíjekyll build --profile.
El primer culpable: el calendario JSON estaba siendo incrustado en cada página
Primero comprobé el tamaño de_site.
_site: 1.2G
HTML total: about 840MB
840MB de HTML para un blog estático.
Eso no tenía sentido.
Abri un puesto representativo, y la razón era visible inmediatamente. El calendario de la barra lateral estaba incrustando el JSON completo post-lista para ese idioma en línea en cada página de correo.
<script type="application/json" data-calendar-posts>
[
...
]
</script>
Esa no era la única cuestión.
Al construir la lista de espera del calendario, Liquid estaba escaneando la lista completa de correos de nuevo para cada fecha. Los mensajes que no coincidían todavía producían una gran cantidad de espacio en blanco líquido. La interfaz de usuario visible era pequeña, pero dentro del HTML había un bloque gigante de espacio en blanco oculto y listas repetidas.
Esto no era un error de características. Era un error estructural.
El calendario no necesitaba que el servidor renderizara una versión HTML completa en cada página. JavaScript ya podía dibujar el calendario de forma dinámica. En ese caso, el servidor solo necesitaba proporcionar la shell básica para el mes actual y una ruta al archivo de datos.
Así que moví los datos del calendario a archivos JSON separados por idioma.
/assets/data/calendar-posts-ko.json
/assets/data/calendar-posts-en.json
/assets/data/calendar-posts-ja.json
/assets/data/calendar-posts-zh-Hans.json
/assets/data/calendar-posts-es.json
/assets/data/calendar-posts-pt-BR.json
/assets/data/calendar-posts-fr.json
/assets/data/calendar-posts-id.json
Y sólo dejó esto en la página.
data-calendar-posts-src="/assets/data/calendar-posts-en.json"
El resultado cayó inmediatamente.
1311.791 seconds -> 745.273 seconds
Casi la mitad del tiempo desapareció, pero aún era largo.
Ahí fue cuando me di cuenta.
El calendario era un gran culpable, pero no era el único culpable.
El segundo culpable: las estadísticas del menú se estaban calculando 80.000 veces
El siguiente perfil era aún más descarado.
_includes/sidebar-nav-stats.html 81120 calls 173.084s
_includes/masthead.html 2704 calls 319.860s
_includes/seo.html 2704 calls 119.985s
sitemap.xml 1 call 117.495s
La parte más divertida fuesidebar-nav-stats.html.
Esto incluye una pequeña pieza que une el conteo de post y el último tiempo de post junto a cada categoría de barra lateral.
Por ejemplo, algo así.
Daily Review (310) 1 days ago
Devlog (24) 2 days ago
Pero cada vez que se llamaba a esta pequeña pieza, se ordenaba la lista de posts entera de nuevo y se filtraba de nuevo.
Para cada elemento del menú en cada idioma.
En cada página.
De nuevo en la barra lateral del escritorio y el menú móvil.
Al final, se llamó 81.120 veces.
Este valor no necesita ser recalculado para cada página. Si el idioma y la URL del menú son los mismos, el resultado es el mismo. Así que lo cambié ainclude_cachedinclude_cached desdejekyll-include-cachejekyll-include-cache .
{% include_cached sidebar-nav-stats.html url=child.url lang=current_lang %}
Entonces el número de llamadas cambió así.
81120 calls -> 210 calls
173 seconds -> 0.4 seconds
Eso se sentía menos como optimización y más como atrapar un error.
El tercer culpable: el sitio siguió escaneando todo para construir enlaces de lenguaje
Mientras agregaba el cambio multilingüe, había puesto lógica como esta enmastheadmasthead,seo, ysitemap.
Does this language URL actually exist?
La intención era correcta.
No debería poner una URL traducida inexistente enhreflango el conmutador de idioma. Así que al principio, comprobé la existencia de URL mediante el bucle a través de todas las páginas y todos los documentos de la colección.
El problema era que esto se repetía en cada página.
2704 pages * site.pages scan * translated collections scan
Esa estructura sólo empeora a medida que crece un sitio multilingüe.
Así que cambié el método.
Este blog ya tiene una regla para las URL traducidas.
/some/post/
/en/some/post/
/ja/some/post/
...
Y las excepciones pueden ser administradas por separado como datos.
Esta vez, puse el post de revisión de la sesión cuya traducción todavía estaba pendiente en_data/i18n_pending.yml.
entries:
- source_url: /devlog/github-pages-blog/github-pages-blog-english-version-lessons/
locales:
- en
- ja
- zh-Hans
- es
- pt-BR
- fr
- id
Con eso, los mensajes normales están conectados por la regla del prefijo, y los mensajes pendientes se vuelven a la página de inicio del otro idioma. No hay un escaneo completo del sitio.
El impacto fue grande.
masthead: 319.860 seconds -> 9.882 seconds
seo: 119.985 seconds -> 7.181 seconds
sitemap: 117.495 seconds -> 4.633 seconds
Y la construcción final terminó así.
done in 110.344 seconds
De 21 minutos 52 segundos a 1 minuto 50 segundos.
Eso todavía no es suficiente para llamar al blog rápido, pero al menos escapó del estado donde estaba demasiado asustado de la construcción para escribir.
Lo que tenía que tener cuidado mientras lo arreglaba
Si sólo miras la velocidad, esta optimización se ve fácil.
Pero la parte que realmente necesitaba cuidado era evitar las características rotas.
En un sitio multilingüe especialmente, es fácil celebrar una construcción más rápida y luego crear problemas como estos.
language switch links go to 404s
hreflang points to nonexistent URLs
pending translations are exposed as alternates to search engines
About/language buttons disappear again on mobile
the calendar stays empty
Así que al final, utilicé tanto cheques de navegador como cheques automatizados.
Estas son las cosas que he comprobado.
production build succeeds
the travel post has working language switch links for 8 languages
hreflang works for 8 languages + x-default
pending translation posts fall back to the other language home
About, 🇺🇸English, and the calendar display correctly on mobile
representative multilingual URLs return 200
the rendered structure of 2481 source posts is checked exhaustively
calendar JSON parses correctly
i18n post coverage errors: 0
No leí todos los mensajes de 2481 uno por uno con ojos humanos.
Pero, como mínimo, la comprobación automatizada cubría si los archivos de salida existen, si la estructura de correo se representa, si los enlaces de idioma no se rompen y si existen datos de calendario.
Ese fue el núcleo de este trabajo.
Construye optimización no sólo se trata de reducir el tiempo. Se trata de volver a los contratos de características existentes.
Lo que aprendí esta vez
Jekyll parece simple porque es un generador de sitio estático.
Pero una vez que Liquid comienza a escanear todo el sitio una y otra vez, incluso un sitio estático puede llegar a ser bastante pesado.
En una estructura multilingüe especialmente, las pequeñas ineficiencias se convierten inmediatamente en multiplicadores.
page count * language count * menu count * total post count
Si ese tipo de multiplicación se esconde en algún lugar, la construcción de repente explotará más tarde.
Lo que aprendí esta vez es simple.
Primero, no inline los mismos datos repetidamente en cada página.
En segundo lugar, la caché incluye cuando la misma entrada produce la misma salida.
Tercero, no escanee todo el sitio desde cada página sólo para comprobar si existen URLs.
Cuarto, mantenga las excepciones como datos en lugar de manejarlos por el tacto.
Quinto, después de la optimización, verificar los contratos de características con cheques automatizados.
Este blog se está convirtiendo lentamente en menos de un simple blog personal y más de un sistema.
Eso es bueno y agotador.
Pero al menos esta vez, la parte agotadora significó algo.
Sacar una construcción que tomó más de 21 minutos en el rango de 1 minuto se sintió como un punto de inflexión bastante grande.
Ahora el blog multilingüe tiene al menos suficiente espacio para respirar para seguir creciendo.
Deja un comentario