Cloudflare
KV
Cloudflare KV
Produção
Padrões
Cache
Limites

KV en producción: los patrones que funcionan y los que engañan al principio

Patrones de uso de Cloudflare KV que funcionan en producción: truco de metadatos, almacenamiento en caché a nivel de módulo, clave de índice para list() y antipatrones que agotan el nivel gratuito rápidamente.

KV en producción: los patrones que funcionan y los que engañan al principio

Mil escrituras al día parecen suficientes hasta que se pone algo en producción con usuarios reales. Un sistema de inicio de sesión que escribe un token de sesión en el KV para autenticación agota este límite con media hora de tráfico moderado. El primer enfrentamiento con los límites reales del KV rara vez ocurre en la puesta en escena.

El nivel gratuito (100.000 lecturas y 1.000 escrituras por día) se diseñó para reflejar el modelo de uso correcto: mucha lectura, mínima escritura. Cuando un equipo usa KV de manera consistente con este modelo, el nivel gratuito dura mucho tiempo. Cuando se utiliza como banco de sesiones o almacén estatal por usuario, el límite aparece en la primera semana.

Patrones que funcionan

El uso más sólido de KV es el almacenamiento de la configuración de la aplicación, leída repetidamente y rara vez modificada. Un JSON con indicadores de productos, parámetros comerciales, listas blancas y puntos finales de terceros: este tipo de datos cambia por acción administrativa, no por usuario. Una escritura en KV se propaga a todos los PoP y atiende millones de solicitudes sin costo relevante. Un equipo que escribe en esta clave diez veces al mes y la lee diez millones de veces se encuentra cómodamente dentro del nivel gratuito.

El almacenamiento en caché del HTML renderizado sigue el mismo principio. Una publicación de blog, una página de producto, un resultado de búsqueda que no cambia por usuario: lo representa una vez, lo guarda en el KV con un TTL apropiado y lo sirve directamente desde la caché de PoP para cualquier solicitud posterior. El costo de renderizado disminuye, la latencia disminuye y la cantidad de escrituras se vuelve proporcional a la frecuencia de las actualizaciones de contenido, no al volumen de tráfico.

Los tokens de sesión con TTL también son válidos, siempre que la sesión sea de una sola escritura. Usted escribe el token tras la autenticación (una escritura por inicio de sesión) y lo lee en cada solicitud posterior. Si un usuario inicia sesión una vez y está activo durante horas, la relación lectura/escritura es excelente. Lo que rompe este modelo es la sesión con estado mutable: cada actualización de datos de la sesión se convierte en una escritura y el costo se dispara.

El truco con los metadatos

Cada clave en el KV puede transportar hasta 1024 bytes de metadatos JSON arbitrarios, separados del valor en sí. Este campo es devuelto por getWithMetadata() junto con el valor, en una sola operación, sin costo de lectura adicional.

El uso práctico es almacenar información junto con el valor que necesitaría analizar o inferir de otra manera. Para un archivo binario guardado en KV, los metadatos pueden contener Content-Type, ETag, fecha de creación, tamaño original y cualquier encabezado HTTP relevante. El trabajador lee la clave, recibe valor y metadatos en una sola llamada y ensambla la respuesta HTTP con los encabezados correctos sin ninguna lógica de análisis o búsqueda adicional.

Esto también funciona para versiones ligeras. Guarde la versión o marca de tiempo de la última actualización en los metadatos. Cualquier consumidor puede comprobar si está leyendo la versión esperada sin buscar un segundo dato.

El problema de rendimiento de la lista()

list() es la operación KV más cara en términos de rendimiento relativo, y es la que aparece con mayor frecuencia en caminos que no deberían utilizarla. Una llamada a list() en un espacio de nombres con 100 mil claves es lenta (la latencia dependerá del tamaño del espacio de nombres y del cursor) y cuenta como una operación de lista, que tiene una cuota separada: mil operaciones por día en el nivel gratuito, 0,50 dólares por millón en el nivel pago.

El verdadero problema es usar list() en la ruta activa de una solicitud. Si cada solicitud necesita descubrir qué claves existen para dar una respuesta, ha colocado una operación de gestión de datos dentro de la ruta de rendimiento crítica.

La solución es mantener una clave de índice. Escribe una clave como __index__ en el KV cuyo valor es un JSON con la lista de claves de espacio de nombres, o simplemente los identificadores necesarios para la lógica. Cuando el espacio de nombres cambia, actualiza el índice junto con la escritura principal. El costo es una escritura adicional por operación de escritura. El beneficio es que cualquier lectura de índice es una lectura normal, con latencia de caché y sin los problemas de escala de list().

Este patrón tiene la limitación obvia de que el índice debe mantenerse sincronizado manualmente. Si tiene varios escritores, la ausencia de operaciones atómicas en KV crea una ventana de inconsistencia en el índice. Para espacios de nombres con escritura única o escritura controlada por un único escritor, el patrón funciona bien.

Almacenamiento en caché a nivel de módulo: la optimización que nadie documenta explícitamente

Los trabajadores de Cloudflare funcionan con aislados V8. Un solo aislamiento puede atender miles de solicitudes antes de ser desalojado. Las variables declaradas en el alcance del módulo (fuera del controlador) persisten entre solicitudes siempre que el aislamiento esté activo.

Esto crea una oportunidad de optimización simple y efectiva para los datos de configuración. En lugar de hacer env.CONFIG.get('settings') en cada solicitud, declara una variable en el alcance del módulo y solo busca el KV cuando aún no se ha inicializado:

let config = null; export default { async fetch(request, env) { config = config ?? await env.CONFIG.get('settings', { type: 'json' }); // usa config } };

La primera solicitud de aislamiento lee el KV. Todas las solicitudes posteriores para el mismo aislamiento utilizan el valor en memoria. Para los datos que rara vez cambian (configuración del producto, indicadores de funciones), esto elimina la lectura KV de casi todas las solicitudes, lo que reduce la latencia y el consumo de operaciones de lectura.

La implicación es que una actualización del KV no se refleja inmediatamente en todos los trabajadores: cada aislado seguirá usando el valor almacenado en caché hasta que sea desalojado. Para datos en los que un retraso de 60 segundos a unos pocos minutos es aceptable, la compensación es excelente. Para los datos que deben actualizarse inmediatamente en todos los trabajadores, este patrón no es adecuado.

Qué no poner en producción sin repensar

Usar KV como cola de trabajos no funciona. Sin operaciones atómicas, dos trabajadores pueden leer el mismo trabajo, procesarlo por duplicado y marcarlo como completo de forma independiente. El resultado es un procesamiento duplicado sin mecanismo de detección.

Guardar datos de usuario mutables mediante clave de usuario no escala correctamente con el límite de escritura. Una aplicación con 10 mil usuarios activos por día que actualiza los datos del perfil incluso una vez por sesión ya está en el orden de magnitud del límite pagado de 1 millón de escrituras mensuales, y el costo por escritura de $0,50/millón comienza a aparecer cuando se va más allá.

Los espacios de nombres con alta densidad de claves y necesidad frecuente de listados son una trampa de rendimiento. list() es lento en espacios de nombres grandes y no debería estar en la ruta de solicitud. Si su caso de uso requiere listados frecuentes, el modelo de datos debe cambiar, ya sea con claves de índice mantenidas manualmente o con una herramienta diferente.

Lo que revela el nivel gratuito sobre el diseño

Los límites del nivel gratuito (100.000 lecturas por 1.000 escrituras) son un documento de diseño disfrazado. La proporción de 100:1 entre lecturas y escrituras no es arbitraria. Describe la carga de trabajo para la que se construyó el KV. Cualquier uso que invierta o acerque esta relación está fuera del modelo operativo previsto y encontrará limitaciones de costo, rendimiento o consistencia que no aparecen en pruebas de bajo volumen.

Lea también