wandres.dev
KV · clave-valor global

TTL, metadata y el caché del edge

Más allá de guardar y leer, KV ofrece tres mecanismos que multiplican su utilidad: la expiración por clave, que hace que un valor desaparezca solo tras un plazo con expirationTtl o en una fecha con expiration; la metadata, un pequeño objeto de contexto que viaja pegado a la clave y se recupera sin una segunda lectura; y el caché en el edge de las lecturas, gobernado por cacheTtl, que explica por qué las claves calientes vuelan y las frías cuestan. Vemos cómo se usan los tres, cómo interactúan con la consistencia eventual, y por qué juntos convierten a KV en algo más que un diccionario.

⏱ 15 min

Un diccionario que solo guarda y devuelve valores ya es útil, pero KV añade tres capacidades que lo elevan. La expiración deja que una clave se borre sola: ideal para sesiones, tokens y cachés que no deben vivir para siempre. La metadata pega a cada clave un pequeño objeto de contexto —quién la creó, de qué tipo es, cuándo caduca su contenido— que recuperas gratis, sin una segunda lectura. Y el caché del edge, que ya intuías en la asimetría lectura/escritura, es ahora algo que puedes afinar con cacheTtl para decidir cuánto tiempo una copia caliente sirve sin volver a consultar el origen. Tres detalles que, bien usados, hacen de KV una herramienta mucho más fina.

🎯 Al terminar esta lección sabrás
  • Expirar claves solas con expirationTtl (plazo relativo) y expiration (fecha absoluta), respetando el mínimo de 60 segundos.
  • Adjuntar y recuperar metadata con put y getWithMetadata, entendiendo su límite de tamaño.
  • Comprender el caché del edge y cómo cacheTtl regula la duración de las lecturas calientes.
  • Ver cómo TTL, metadata y caché interactúan con la consistencia eventual del nivel.

Expiración por clave: expirationTtl y expiration

KV puede borrar una clave por ti al cabo de un tiempo, sin que tengas que programar nada. Hay dos formas de decirlo, y solo se diferencian en cómo expresas el momento. expirationTtl toma un plazo relativo en segundos —“caduca dentro de este tiempo”— y expiration toma una fecha absoluta como marca de tiempo Unix en segundos —“caduca en este instante”—. El plazo mínimo es de 60 segundos: KV no expira nada antes de ese umbral.

// caduca en una hora a partir de ahora (plazo relativo)
await env.MI_KV.put("sesion:abc", datos, { expirationTtl: 3600 });

// caduca en un instante concreto (fecha absoluta, Unix en segundos)
const medianoche = Math.floor(new Date("2026-08-01T00:00:00Z").getTime() / 1000);
await env.MI_KV.put("promo:verano", datos, { expiration: medianoche });

La expiración es la forma natural de modelar todo lo que tiene fecha de caducidad: sesiones de usuario, tokens de un solo uso, respuestas de API cacheadas, banners de una campaña. Delegar el borrado en KV es más fiable que un cron que barre claves viejas, y no cuesta nada. Eso sí, la expiración también es eventualmente consistente: una clave caducada puede tardar un poco en desaparecer de todos los cachés, así que no la uses como un temporizador exacto ni para revocar algo crítico al segundo.

Metadata: contexto pegado a la clave

Al escribir puedes adjuntar un objeto de metadata: un pequeño JSON que viaja con la clave y que recuperas junto al valor —o al listar— sin pagar una segunda lectura. Es perfecto para datos sobre el dato: la versión, el tipo de contenido, quién lo escribió, cuándo expira lógicamente su contenido. El límite es modesto —del orden de 1024 bytes serializados—, porque la metadata está pensada para etiquetas, no para carga útil.

await env.MI_KV.put("logo.png", bytes, {
  metadata: { contentType: "image/png", version: 3, subidoPor: "ada" },
});

// una sola llamada devuelve valor Y metadata
const { value, metadata } = await env.MI_KV.getWithMetadata("logo.png", {
  type: "stream",
});

El valor de la metadata se nota sobre todo con list: cada clave listada trae su metadata incorporada, de modo que puedes decidir qué claves te interesan —por su tipo, su versión o su estado— sin hacer un get por cada una. Recorrer mil claves para leer una etiqueta de cada una sería mil lecturas; con metadata en el listado, es una sola operación. Es la diferencia entre hojear un fichero por sus pestañas y abrir cada carpeta para mirar dentro.

El caché del edge y cacheTtl

Ya sabes que la primera lectura de una clave en una región va al origen y las siguientes se sirven de una copia caliente en el edge. cacheTtl te deja controlar cuánto dura esa copia: es el número de segundos —mínimo 60— que un centro de datos servirá el valor cacheado antes de volver a consultar el origen para refrescarlo. Subirlo hace las lecturas más rápidas y baratas a costa de tolerar datos más viejos; bajarlo refresca antes a costa de más viajes al origen.

// tolera hasta 5 minutos de valor viejo a cambio de lecturas mas calientes
const config = await env.MI_KV.get("config:activa", {
  type: "json",
  cacheTtl: 300,
});
flowchart LR
R[get de una clave] --> C{copia caliente en este edge}
C -- si dentro de cacheTtl --> H[respuesta en un milisegundo]
C -- no o cacheTtl vencido --> O[consulta al origen]
O --> W[guarda copia caliente y responde]
style H fill:#a6e3a1,color:#11111b
style O fill:#fab387,color:#11111b
style C fill:#89b4fa,color:#11111b

La consecuencia práctica es una regla de rendimiento clara: KV premia a las claves populares y penaliza a las raras. Una clave leída sin cesar vive caliente en casi todos los edges y responde en un milisegundo; una clave leída una vez al día casi siempre encuentra el caché frío y paga el viaje al origen. Por eso KV brilla con pocos datos muy leídos y rinde mal con muchísimos datos leídos cada uno rara vez. Y nota cómo cacheTtl se suma al desfase de propagación: si toleras cinco minutos de caché, una escritura puede tardar esos cinco minutos —más la propagación— en verse en una región que tenía la copia caliente.

💡
Elige cacheTtl según cuánta caducidad tolera el dato

No hay un cacheTtl universalmente bueno. Para datos que casi nunca cambian y se leen mucho —una tabla de rutas, una configuración estable— súbelo con confianza: ganas velocidad y ahorras coste. Para datos que cambian con cierta frecuencia y donde un valor muy viejo molesta, mantenlo bajo. La pregunta es siempre la misma que en todo KV: ¿cuántos segundos de desactualización puede permitirse este dato en concreto?

TTL, metadata y cacheTtl son la confesión de que el tiempo es el material de KV

Si miras estas tres funciones a la vez, descubres que ninguna trata sobre el valor guardado: las tres tratan sobre el tiempo. La expiración decide cuándo un dato deja de existir; el cacheTtl decide cuánto tiempo una copia se considera fresca; y la metadata, en su uso más revelador, suele llevar precisamente marcas temporales y de versión que dicen a qué momento pertenece el valor. Esto no es casualidad: en un almacén distribuido y eventualmente consistente, el tiempo no es un detalle administrativo sino la dimensión que estructura todo el sistema. En una base de datos de una sola máquina el tiempo casi desaparece —hay un antes y un después nítidos, marcados por transacciones que ocurren en instantes bien ordenados—. En cuanto repartes los datos por el planeta, esa nitidez se disuelve: ya no hay un único “ahora”, sino tantos relojes como cachés, cada uno viendo una versión de la verdad con una antigüedad distinta. Gestionar KV con maestría es, en el fondo, gestionar el tiempo de tus datos: decidir cuánta antigüedad tolera cada clave, cuánto ha de vivir, cuándo debe morir sola, y a partir de qué instante una copia deja de ser digna de confianza. Los tres mecanismos de esta lección son las palancas con las que expresas esas decisiones. Y hay una simetría hermosa que conviene ver: expiración y cacheTtl son la misma idea aplicada en dos capas —“este valor caduca” en el almacén, “esta copia caduca” en el caché—, mientras la metadata te da el vocabulario para razonar sobre ambas sin abrir el valor. El ingeniero que trata el tiempo como el material principal de KV —y no como un ajuste secundario que se toca al final— deja de sufrir las sorpresas del desfase y empieza a orquestarlas: sabe exactamente cuánta realidad vieja circula por su sistema en cada momento, porque él mismo puso ese número.

⚔️ Orquesta el tiempo de tus claves
  1. Modela una sesión de usuario que caduque sola en 30 minutos y explica por qué expirationTtl es mejor aquí que un cron que borre sesiones viejas.
  2. Usa expiration con una fecha absoluta para un contenido de campaña que debe desaparecer a medianoche, y razona la diferencia con expirationTtl.
  3. Adjunta metadata a un binario y demuestra, con list, cómo filtrar claves por su tipo o versión sin hacer un get por cada una.
  4. Elige un cacheTtl para dos datos distintos —uno estable, uno cambiante— y justifica cada número en términos de tolerancia a la antigüedad.
  5. Explica cómo se suman cacheTtl y el desfase de propagación para determinar cuánto tarda, en el peor caso, una escritura en verse en una región con la copia caliente.