Infrastructure and SEO

Upstash Redis: los 5 patrones que usamos en cada SaaS en 2026

Rate limiting, sesiones, cache-aside, locks distribuidos y colas QStash. Los cinco patrones Upstash Redis que cableamos en cada SaaS, con costes reales.

26 de mayo de 20268 min de lectura
Server rack with blinking green lights

Upstash Redis es un Redis serverless de pago por petición con acceso HTTP que corre dentro de Vercel, Cloudflare Workers y AWS Lambda sin connection pooling. Lo usamos en cada SaaS que construimos porque cinco patrones se repiten en cada proyecto: rate limiting, sesiones, cache-aside en lectura, locks distribuidos para idempotencia de webhooks y colas QStash para trabajos en segundo plano. Los patrones no son exóticos. Lo que cambia con Upstash es que cada uno se escribe en pocas líneas sin provisionamiento, y el coste solo se mueve cuando se mueve el tráfico.

Qué medimos antes de elegir estos cinco

Usamos Redis en una docena de SaaS en producción en 2026. Los patrones de abajo son los que aparecen en al menos ocho. Todo lo demás (pub/sub, leaderboards, índices vectoriales) lo guardamos aparte. Los criterios fueron simples: el patrón tiene que resolver un problema real en cada SaaS, llegar a producción en menos de un día y o ahorrar más de lo que cuesta o desbloquear una función que requeriría un servicio propio.

1. Rate limiting con ventana deslizante usando @upstash/ratelimit

Cada endpoint público necesita un techo. Sin él, un cliente que se porta mal (o un scraper, o el stress test de un competidor) te vacía el origen, el pool de conexiones a la base de datos y la factura. La librería oficial, @upstash/ratelimit, ofrece algoritmos sliding-window, token-bucket y fixed-window con unos 100 bytes por clave: incluso una instancia Upstash de 256MB aguanta millones de claves activas.

Lo que cableamos de verdad:

  • Tres niveles por familia de endpoint: anónimo (techo bajo, rechazo rápido), autenticado gratis (medio), autenticado de pago (alto). El identificador es la IP para el anónimo, el user id para los autenticados.
  • Ventana deslizante, porque las fijas dejan que un pico en la frontera duplique el límite.
  • Caché efímera en memoria para las claves ya bloqueadas: cuando una clave está sobre el límite, recordamos el reset time en memoria de proceso y rechazamos la siguiente petición sin ir a Redis. En un endpoint bajo ataque, baja los comandos Upstash un 80% o más.
  • La comprobación corre en el middleware de Next.js: el rechazo llega antes del route handler, antes de la base de datos y antes de cualquier llamada a un LLM.

Forma del coste: una comprobación son dos comandos Redis. A 0,2 dólares por 100K comandos en pago por petición, las cuentas son triviales hasta que cruzas los 5 millones de comprobaciones diarias, momento en que un plan fijo empieza a ganar.

2. Sesiones con TTL

Si autenticas usuarios en el edge, tu session store no puede ser Postgres. Volver al origen desde un Cloudflare Worker o una Edge Function anula el sentido de estar allí. Upstash guarda la sesión bajo una clave con el hash del token, fija un TTL con EXPIRE y sirve la lookup en pocos milisegundos desde cualquier región.

La forma que usamos:

  • Clave: session:{token_hash}. El token es un valor aleatorio de 32 bytes guardado en una cookie HTTP-only. Guardamos el hash, nunca el token en crudo.
  • Valor: un JSON de 1 a 4 KB con user id, rol, id de organización y feature flags resueltos en el login. Lo que pesa más vuelve a Postgres bajo demanda.
  • TTL: 30 días para “recúrdame”, 24 horas por defecto. Refresh deslizante en cada petición autenticada con EXPIRE, con un techo duro para que un atacante no mantenga vivo un token robado para siempre.
  • El logout es un DEL. El cierre forzado en todos los dispositivos es un SCAN por prefijo del usuario, fuera del camino caliente.

Capacidad: a unos 2 KB por sesión, una instancia Upstash de 1 GB aguanta del orden de 500.000 sesiones activas. La mayoría de los SaaS B2B que enviamos viven en el plan gratuito el primer año.

3. Cache-aside para lecturas calientes

El tercer patrón es cache-aside de manual: en lectura, consultas Redis primero; si hay miss, vas a Postgres y reescribes el resultado con un TTL. Lo usamos para entidades muy leídas y poco escritas: configuraciones de feature flags, páginas de perfil públicas, catálogos de producto y el payload de “usuario actual” que hidrata la navbar en cada page load.

La disciplina que mantiene vivo cache-aside en producción:

  • TTL en cada clave. Sin excepciones. Una caché sin TTL se convierte en una fábrica de datos rancios el día que algo falla en la invalidación.
  • Invalidación explícita en los write paths: cuando la fila subyacente cambia, DEL de la clave de caché desde la misma transacción o desde el worker de la cola que cierra la escritura.
  • Stale-while-revalidate donde la frescura puede esperar. Devuelves el valor cacheado al instante y refrescas en segundo plano, con una ventana de staleness corta. Las lecturas se quedan por debajo de 50ms aunque la query de origen tarde 300.
  • Nunca cachear nada que varíe por usuario bajo una clave compartida. El número de incidentes de cache poisoning que empiezan con “pero en staging funcionaba” es, por experiencia, exactamente el número de veces que alguien olvida esta regla.

4. Lock distribuido para idempotencia de webhooks

Stripe, Resend, Clerk y cualquier otro vendor reenvían un webhook si tu endpoint da timeout, falla o devuelve el código equivocado. Si el handler hace algo no idempotente (crear una fila, mandar un email, cobrar una cuenta), un reenvío sin guardia duplica el efecto. La respuesta clásica es un constraint unique sobre una idempotency key en Postgres. La respuesta más rápida, en serverless, es un lock Redis.

Usamos @upstash/lock, que envuelve la primitiva SET key value NX EX seconds en una API tipada. Al recibir el webhook intentamos adquirir un lock con clave en el event id y TTL de 60 segundos. Si el lock está tomado, devolvemos 200 al instante y dejamos al handler en vuelo hacer su trabajo. Si lo adquirimos nosotros, ejecutamos el handler, persistimos el resultado y soltamos el lock.

Dos cautelas importan:

  • Upstash Redis replica de forma asíncrona entre réplicas. En una partición de red, dos clientes pueden mantener brevemente el mismo lock. El patrón vale para rendimiento y para la mayoría de los casos de idempotencia, no para claves primarias financieras. Cuando hace falta exclusión mutua estricta, el lock es el chequeo optimista y el constraint unique de la base de datos es la guardia autoritativa.
  • El TTL del lock tiene que superar el peor runtime del handler. Si no, un handler lento suelta su propio lock a mitad de camino y un reenvío se cuela.

La misma primitiva sirve para evitar cache stampede, hacer debounce de acciones de usuario como “reenviar invitación” y serializar trabajos de fondo que no deben solaparse.

5. Trabajos en segundo plano con QStash

QStash no es Redis. Es un producto hermano: una cola de mensajes HTTP con reintentos, scheduling, fan-out y dead-letter. Está en esta lista porque la pregunta “cómo hago un trabajo en background desde una función serverless” aparece en cada SaaS, y QStash es la respuesta a la que volvemos.

Por qué vamos por defecto a QStash en vez de a una cola Redis que nos mantenemos:

  • Sin workers que alojar. QStash hace POST a un endpoint HTTPS que ya operas. El endpoint es un route handler Next.js corriente, un Cloudflare Worker, una Lambda. La autenticación es un JWT firmado en la cabecera.
  • Reintentos, backoff exponencial y DLQ incluidos. Ya no escribimos lógica de retry.
  • El scheduling es de primera clase: cron o timestamps concretos, sin servicio aparte.
  • El timeout de endpoint de 60 segundos es la única restricción real. Cualquier cosa más larga (ETL grandes, procesado de vídeo, fine-tuning de modelos) va a otro runtime, normalmente un contenedor long-lived.

El precio es 1 dólar por 100K mensajes por encima del plan gratuito de 500 al día. El default correcto para trabajo en background serverless por debajo de 1M de jobs al día.

Dónde se da la vuelta la forma del coste

La pregunta más repetida sobre Upstash es “¿cuándo deja de ganar el pago por petición?”. Nuestra heurística, validada en los SaaS que operamos:

  • Por debajo de 500K comandos diarios entre todos los patrones, el pago por petición es más barato que cualquier alternativa managed que hemos puesto en precio.
  • Entre 500K y 5M comandos diarios empiezan a ganar los planes fijos. Pasamos a una instancia Upstash de plan fijo de 10 dólares al mes y nos quedamos ahí.
  • Por encima de 10M comandos diarios sobre un solo workload, Redis managed tradicional (ElastiCache, Redis Cloud o una alternativa regional) sale más barato, a veces en un orden de magnitud. DanubeData publicó las cuentas del breakpoint en mayo de 2026: un rate-limiter de 10M comandos diarios cuesta unos 600 dólares al mes en pago por petición frente a unos 10 euros al mes en una instancia fija pequeña.

Mira el volumen por patrón, no el agregado. Rate limiting y cache-aside cruzan el umbral primero; sesiones y locks casi nunca.

Lo que no metemos en Upstash a propósito

Tres cosas se quedan fuera de Redis aunque sea técnicamente posible: cualquier cosa que necesite ACID estricto, cualquier cosa en la que el dato sea la fuente autoritativa y cualquier workload medido en latencia p99 de un solo dígito de milisegundos que cruce regiones en cada llamada. Upstash es rápido, no mágico. Las dos primeras viven en Postgres. La tercera vive a menudo en una caché embebida colocada con la función.

Cinco patrones, una sola dependencia, una forma de coste predecible. Cuando hacemos el onboarding de un nuevo SaaS, cablear estos cinco es media jornada de trabajo y elimina una clase recurrente de decisiones para el resto del proyecto.

Foto de Domaintechnik Ledl.net en Unsplash

Preguntas frecuentes

When does Upstash Redis stop being cheaper than managed Redis?
Pay-per-request wins below roughly 500K commands per day across all patterns. Between 500K and 5M, a $10/month Upstash fixed-plan instance is the better fit. Above 10M commands per day on a single workload, traditional managed Redis (ElastiCache, Redis Cloud, regional alternatives) becomes cheaper, sometimes by an order of magnitude. Track the per-pattern volume, not the aggregate: rate limiting and cache-aside cross the threshold first.
Is an Upstash Redis lock safe for financial idempotency?
Not on its own. Upstash replicates asynchronously between replicas, so on a network partition two clients can briefly hold the same lock. The pattern is fine for performance and most idempotency cases. For financial primary keys (charges, payouts, refunds), use the lock as an optimistic check and a Postgres unique constraint on the idempotency key as the authoritative guard. The lock prevents the work; the constraint prevents the duplicate.
Why pick QStash over a Redis-based queue we run ourselves?
Three reasons. There are no workers to host: QStash POSTs to an HTTPS endpoint you already operate (a Next.js route, a Worker, a Lambda). Retries, exponential backoff, and dead-letter queues are built in, so you stop writing retry code. Scheduling is first-class through cron or specific timestamps, removing the need for a separate scheduler. The 60-second endpoint timeout is the one real constraint; jobs that need longer should run on a long-lived container, not a queue.
What should we deliberately keep off Upstash Redis?
Three things. Anything that needs strict ACID transactions belongs in Postgres. Anything where Redis would be the system of record belongs in Postgres, because cache data can vanish on eviction or a TTL bug. Any workload measured in single-digit-millisecond p99 latency that crosses regions on every call belongs in an embedded cache colocated with the function, not in a network-bound store. Upstash is fast, not magic.

Studio

Empieza un proyecto.

Un partner único para el producto digital que necesitas construir. Producción más rápida, tecnología moderna, costes reducidos. Un equipo, una factura.