Tiered Cache: capas para maximizar los aciertos
Cientos de centros de datos con cachés independientes multiplican los fallos y la carga sobre el origen. Tiered Cache los organiza en capas para que un fallo local se resuelva en otro nodo de Cloudflare antes de molestar a tu servidor; con ello, el TTL efectivo y las cabeceras que de verdad mandan sobre cuánto vive una respuesta.
La propiedad que hace rápida a la cache del edge —que está pegada al usuario— es la misma que la hace ineficiente a escala: si cada centro de datos guarda su propia copia, cada centro de datos tiene que fallar al menos una vez para conseguirla. Con una red de cientos de emplazamientos, una única URL popular puede generar cientos de peticiones a tu origen antes de que la red esté caliente, y cada expiración reinicia el ciclo. Tiered Cache es la respuesta arquitectónica a ese problema: en lugar de una constelación plana de cachés que se ignoran, Cloudflare las organiza en niveles, de modo que un nodo que falla pregunta primero a otro nodo de la red y solo el último eslabón habla con tu servidor. Entender esa jerarquía —y las cabeceras que deciden cuánto dura cada entrada en ella— es lo que separa una tasa de aciertos aceptable de una excelente.
- Explicar por qué una red de cachés independientes diluye la tasa de aciertos y multiplica la carga en el origen.
- Distinguir las topologías de Tiered Cache y el papel de la capa regional.
- Determinar el TTL efectivo de una respuesta según el orden de precedencia de reglas y cabeceras.
- Diagnosticar el comportamiento real de la cache leyendo
cf-cache-statusyage.
El coste de una red plana
Sin jerarquía, cada centro de datos es una isla. La primera petición que llega a Madrid falla y va a tu origen; la primera que llega a Santiago falla y va a tu origen; lo mismo en Singapur, en Dubái y en cada punto donde aparezca un usuario. La tasa de aciertos que ves en el panel es un promedio que oculta esa fragmentación: puedes tener un ochenta por ciento global y aun así estar sirviendo la misma respuesta desde el origen decenas de veces por minuto, porque el conjunto de nodos activos crece con tu audiencia y cada uno se calienta por su cuenta.
El problema empeora con la vida útil. Un recurso con un TTL corto expira en todas partes y vuelve a exigir un viaje al origen desde cada isla, así que el tráfico que llega a tu servidor no es proporcional al número de usuarios sino al número de centros de datos multiplicado por la frecuencia de expiración. Es exactamente el escenario donde una cache mal diseñada se convierte en un amplificador de carga en lugar de un amortiguador.
Conviene hacer el cálculo una vez para que la intuición quede grabada. Supón un recurso con un TTL de sesenta segundos servido desde doscientos emplazamientos activos: son doscientas peticiones al origen por minuto, doce mil por hora, y esa cifra no depende en absoluto de si tienes mil usuarios o un millón. La carga sobre tu servidor está determinada por la geometría de la red y por tu TTL, no por tu audiencia. Es un resultado contraintuitivo y explica por qué muchos equipos ven un origen sospechosamente ocupado justo cuando acaban de instalar una CDN.
Reducir un TTL de sesenta a diez segundos no multiplica por seis la carga de un servidor: la multiplica por seis en cada uno de los cientos de emplazamientos a la vez. Antes de acortar una vida útil por comodidad, calcula el tráfico resultante contra el origen y considera si lo que buscabas era frescura o simplemente no haber pensado en la invalidación, que es un tema del final del nivel.
Cómo se organizan las capas
Hay además un efecto de segundo orden que agrava la fragmentación: cuanto más se distribuye tu audiencia, peor funciona tu cache. Un producto que crece geográficamente activa emplazamientos nuevos, y cada emplazamiento nuevo aporta usuarios que fallan y muy pocos que aciertan, porque el volumen por nodo es bajo. El éxito internacional deteriora la tasa de aciertos justo cuando el equipo esperaba lo contrario, y la explicación no está en el código sino en la aritmética de repartir un mismo tráfico entre más cachés.
Tiered Cache introduce una distinción de roles. Los nodos que atienden usuarios pasan a ser capa inferior; un subconjunto de emplazamientos grandes y bien conectados actúa de capa superior. Cuando la capa inferior falla, no sale a Internet: pregunta a su capa superior, que suele tener el recurso porque concentra los fallos de muchas islas. Solo si esa capa también falla se produce la petición a tu origen. El resultado es doble: tu servidor ve una fracción del tráfico anterior, y los rellenos entre nodos de Cloudflare viajan por la red privada, más rápida y estable que la ruta pública.
Smart Tiered Cache
Cloudflare elige dinámicamente la capa superior más cercana a tu origen midiendo latencia. Configuración de una sola casilla y el punto de partida sensato para casi todo el mundo.
Generic Global
Un conjunto fijo de emplazamientos grandes hace de capa superior para todo el mundo. Menos adaptativo, más predecible.
Regional Tiered
Añade un escalón intermedio por región. Compensa cuando tu audiencia está muy repartida o los objetos son grandes, a costa de un salto más.
Elegir entre esas topologías no requiere teoría, solo saber dónde está tu origen y dónde está tu gente. Si tienes un único origen en una región concreta, la variante inteligente acierta prácticamente siempre, porque su criterio —acercar la capa superior al origen— es exactamente el que reduce el coste del relleno. Si sirves objetos grandes a una audiencia repartida por varios continentes, el escalón regional compensa su salto adicional al evitar que cada continente cruce el planeta para un fallo. Y si tu origen ya está distribuido, la jerarquía aporta menos porque el viaje que ahorra era corto de todas formas.
flowchart TD U1[Usuario en Lima] --> L1[Capa inferior Lima] U2[Usuario en Oslo] --> L2[Capa inferior Oslo] L1 -->|fallo| R[Capa regional opcional] L2 -->|fallo| R R -->|fallo| S[Capa superior] S -->|fallo| O[Tu origen] S -->|acierto| R style S fill:#89b4fa,color:#11111b style O fill:#f38ba8,color:#11111b
Hay un matiz que conviene fijar desde el principio: esta jerarquía pertenece al pipeline de la CDN, es decir, actúa sobre las subpeticiones que tu Worker hace con fetch hacia el origen. Lo que escribes a mano con put vive en la cache del centro de datos que ejecutó el Worker. En la práctica esto dibuja una regla de diseño clara: para respuestas que vienen de un origen, apóyate en el pipeline y deja que la jerarquía trabaje; reserva la escritura manual para lo que tu Worker calcula y nadie más puede darte.
La capa superior aporta además un beneficio que no se ve en el diagrama y que a veces importa más que la tasa de aciertos: la agrupación de peticiones concurrentes. Cuando muchos nodos inferiores fallan a la vez sobre el mismo recurso, la capa superior no repite el viaje al origen por cada uno; consolida esas solicitudes en un relleno y reparte el resultado. La estampida que estudiaste en la lección anterior queda así contenida dentro de la red de Cloudflare en lugar de descargarse sobre tu servidor.
const respuesta = await fetch(peticion, {
cf: {
cacheEverything: true,
cacheTtlByStatus: { "200-299": 3600, "404": 60, "500-599": 0 },
},
});
Ese objeto cf es la puerta de entrada al pipeline desde tu código: cacheEverything obliga a considerar cacheable un recurso que por su extensión no lo sería, y cacheTtlByStatus fija vidas distintas según el resultado, algo especialmente valioso para no cachear errores del servidor y sí cachear brevemente los recursos ausentes.
El TTL efectivo: quién manda sobre quién
La pregunta «¿cuánto vive esto en la cache?» casi nunca tiene una respuesta única, porque hay varias fuentes que pueden fijarla y un orden de precedencia que las arbitra. En lo más alto están las reglas de cache de la zona y las opciones que tu Worker pasa en el objeto cf de un fetch, que sobrescriben lo que diga el origen. Por debajo, las cabeceras específicas de CDN, que existen precisamente para separar la política del navegador de la política del edge. Y al final, las cabeceras HTTP clásicas.
| Fuente | Alcance | Nota |
|---|---|---|
Reglas de cache y opciones cf |
Solo el edge | Ganan sobre cualquier cabecera del origen |
Cloudflare-CDN-Cache-Control |
Solo Cloudflare | La más específica de las tres de CDN |
CDN-Cache-Control |
Cualquier CDN | Ignorada por el navegador |
Cache-Control |
Edge y navegador | s-maxage | max-age según quién lea |
Expires |
Edge y navegador | Alternativa antigua, menor precedencia |
Esa separación entre CDN-Cache-Control y Cache-Control resuelve una tensión muy real: quieres que el edge guarde una respuesta cinco minutos y que el navegador no la guarde nada, porque el edge lo puedes purgar y el navegador del usuario no. Con una sola cabecera es imposible expresarlo; con dos, es una línea. La regla mental es que cuanto más específica es la cabecera para el consumidor, más manda para ese consumidor.
Ese asimetría entre edge y navegador es, en el fondo, una diferencia de poder sobre el almacén. La copia que vive en el edge te pertenece: puedes purgarla, medirla y sustituirla cuando quieras. La copia que vive en el disco del usuario está fuera de tu alcance para siempre, y ninguna herramienta la alcanza hasta que su TTL vence. De ahí una heurística que casi nunca falla: sé generoso con los tiempos que controlas y tacaño con los que no. Un s-maxage de horas acompañado de un max-age de cero suele ser mejor decisión que repartir el tiempo a partes iguales, porque el usuario sigue obteniendo respuestas casi instantáneas desde el edge y tú conservas la capacidad de rectificar.
Las opciones de vida útil del navegador y de cacheo por extensión que se fijan en el panel actúan como valores por omisión o como sobrescrituras según cómo estén configuradas. Cuando el TTL observado no coincide con el que emiten tus cabeceras, el sospechoso número uno no es el código sino una regla de la zona que alguien creó meses atrás y que nadie recuerda. Revisar esa configuración es siempre el primer paso del diagnóstico.
max-age habla a todas las cachés; s-maxage habla solo a las compartidas, que es lo que es el edge. Un Cache-Control: public, max-age=0, s-maxage=600 significa literalmente «navegador, revalida siempre; edge, guárdalo diez minutos». Es la forma más portable de conseguir la asimetría que casi siempre quieres, sin depender de cabeceras propietarias.
Leer lo que de verdad pasó
Ninguna de estas decisiones es verificable por razonamiento: hay que mirarlas. Cloudflare devuelve cf-cache-status en cada respuesta y ese valor es el diagnóstico más honesto que existe. Un HIT dice que se sirvió de cache; MISS, que hubo que rellenar; EXPIRED, que había copia pero caducada; STALE, que se sirvió caducada a propósito; REVALIDATED, que se confirmó con el origen sin reenviar el cuerpo; BYPASS, que alguna regla impidió cachear; y DYNAMIC, que la respuesta ni siquiera era candidata según la configuración de la zona.
Distinguir MISS de EXPIRED es más útil de lo que parece: el primero dice que nunca hubo copia en ese nodo, y se corrige mejorando la tasa de aciertos o activando la jerarquía; el segundo dice que la hubo y venció, y se corrige alargando el TTL o tolerando contenido caducado. Son dos problemas distintos con dos soluciones distintas, y confundirlos lleva a subir vidas útiles cuando lo que hacía falta era Tiered Cache, o al revés.
La cabecera age completa el cuadro indicando cuántos segundos lleva esa copia en la cache. Con las dos juntas se diagnostica casi todo: un MISS permanente sobre un recurso que debería cachearse apunta a un Set-Cookie inesperado o a una regla de bypass; un HIT con un age que nunca crece señala que estás mirando nodos distintos en cada petición; un DYNAMIC obstinado suele significar que falta activar el cacheo para ese tipo de contenido.
Una advertencia metodológica que ahorra horas: al medir desde tu propia máquina estás midiendo un único emplazamiento, casi siempre el mismo, y la primera petición del día siempre fallará. Ninguna conclusión sobre la tasa de aciertos global puede extraerse de ese experimento. Para juzgar de verdad hay que mirar las analíticas de la zona, que agregan lo que ocurre en toda la red, y contrastarlas con el tráfico que registra tu origen. La regla es sencilla: cf-cache-status sirve para depurar el comportamiento de una respuesta concreta; las analíticas sirven para juzgar una política.
const salida = new Response(cuerpo, respuestaBase);
salida.headers.set("x-colo", String(request.cf?.colo ?? "desconocido"));
salida.headers.set("x-cache-worker", acierto ? "HIT" : "MISS");
salida.headers.set("x-upstream", respuestaBase.headers.get("cf-cache-status") ?? "n/a");
Con esas tres cabeceras tienes el cuadro completo de una petición concreta: en qué emplazamiento se atendió, si tu propia cache-aside acertó, y qué dijo el pipeline de la CDN por debajo. Es la instrumentación mínima que debería llevar cualquier Worker que cachee, y su coste es despreciable frente a las horas que ahorra el día que alguien informa de que ve contenido antiguo.
Es perfectamente normal que una respuesta atraviese tu cache-aside y además el pipeline de la CDN, con estados distintos en cada capa. Un HIT tuyo con un cf-cache-status de MISS significa que resolviste sin salir; el caso inverso significa que tu clave no acertó pero la CDN sí evitó el viaje al origen. Leer las dos señales juntas es lo que permite atribuir cada mejora a la capa que la produjo.
Hay una idea que un ingeniero encuentra una y otra vez a lo largo de su vida, siempre disfrazada, y Tiered Cache es una de sus apariciones más elegantes. La idea es que ningún sistema rápido es plano. Un procesador no tiene una memoria, tiene L1, L2, L3 y RAM, porque la única manera de ser simultáneamente rápido y grande es escalonar: capas pequeñas y cercanas que absorben la mayoría de los accesos, capas grandes y lejanas que absorben el resto. Cloudflare no ha inventado nada nuevo aquí; ha reconocido que una red de trescientos emplazamientos tiene exactamente la misma tensión que una jerarquía de memoria, y ha aplicado la misma solución a escala planetaria. Lo interesante es lo que eso implica para tu razonamiento cuando diseñas. En una arquitectura plana, la pregunta relevante es «¿está cacheado?», una pregunta binaria y pobre. En una jerárquica, la pregunta correcta es «¿a qué distancia está?», y esa admite una respuesta continua que te permite razonar sobre coste, sobre carga y sobre la forma de tu tráfico. Un fallo en la capa inferior que se resuelve en la superior no es un fallo: es un acierto más caro. Un fallo que llega al origen sí lo es, y su coste no es la latencia sino la carga acumulada de todos los nodos que fallaron a la vez. Cuando interiorizas esa métrica —distancia al dato, no presencia del dato— empiezas a diseñar de otra manera: alargas los TTL de lo que puedes purgar, acortas los de lo que no, separas la política del edge de la del navegador porque son consumidores con contratos distintos, y dejas de pensar en tu origen como el servidor que atiende usuarios para pensarlo como la última capa de una jerarquía que casi nunca debería tocarse. Ese cambio de marco es el que convierte una web rápida en una web que además es barata y que no se cae el día que se hace popular.
- Sirve un recurso con
Cache-Control: public, max-age=0, s-maxage=600y verifica con las herramientas de red que el navegador revalida mientras el edge devuelveHITcon unagecreciente. - Pide la misma URL desde tres regiones distintas y anota el
cf-cache-statusde la primera petición de cada una; explica el patrón que observas. - Activa Tiered Cache y compara el número de peticiones que recibe tu origen durante una hora de tráfico equivalente.
- Fuerza un
BYPASSañadiendoSet-Cookiea la respuesta y después arréglalo sin perder la cookie, razonando qué opción es aceptable en producción. - Construye una respuesta que dure diez minutos en el edge y cero en el navegador usando primero
s-maxagey despuésCDN-Cache-Control; argumenta cuál preferirías mantener a cinco años vista.