Cómo se escala un sistema para manejar 100K requests por segundo (Caso TinyURL)
Cuando pensamos en un acortador de URLs como TinyURL o Bitly, lo imaginamos como algo simple: recibes un enlace largo, generas uno corto y lo devuelves al usuario. Pero esa sencillez desaparece cuando la plataforma necesita procesar 100,000 enlaces por segundo, con garantías de unicidad, baja latencia y alta disponibilidad.
Esto es exactamente lo que ocurrió con Rebrandly, un servicio de gestión de enlaces que desarrolló una arquitectura capaz de generar 100K URLs en un solo segundo. El reto no fue programar el acortador, sino diseñar un sistema distribuido, seguro y altamente escalable.
En este artículo veremos las distintas soluciones, por qué algunas fallan y cuál es la arquitectura correcta para escalar a ese nivel.
✅ El problema real
Funcionalmente, un acortador debe:
- Convertir una URL larga → URL corta
- Recuperar la URL original a partir del alias
- Soportar dominios personalizados
Pero la parte más difícil está en los requisitos no funcionales:
- La URL corta debe ser única
- La consulta debe ser rápida (< 100 ms)
- El sistema debe procesar 100,000 requests por segundo
- Alta disponibilidad y tolerancia a fallos
Diseños simples como un solo servidor o una sola base de datos se caen inmediatamente.
✅ Primer enfoque: procesamiento asíncrono
Una idea inicial es procesar de forma asíncrona:
- El servidor genera la short URL y responde al cliente
- El mensaje se envía a una cola (ej: SQS)
- Lambdas o workers escriben en la base de datos
✅ Ventajas:
- Respuesta rápida
- Escalable con más workers
❌ Problema crítico:
- No garantiza unicidad. Dos servidores pueden generar el mismo alias antes de insertarlo.
Este enfoque es rápido, pero incorrecto.
✅ Segundo enfoque: verificación síncrona
Otro intento es validar antes de guardar:
- Consultar si el alias existe
- Si no existe, insertar
Funciona, pero es lento.
Cada petición requiere una lectura + escritura. En bases como DynamoDB esto implica ~10 ms extras.
Multiplica eso por 100,000 operaciones por segundo y obtienes:
- Latencia acumulada
- Saturación de la base
- Riesgo de timeout y fallos
Este diseño es correcto, pero no escalable.
✅ Solución final: procesamiento distribuido + transacciones
El diseño ganador combina tres ideas:
✔ 1. Dividir la carga
En lugar de que un servidor procese 100K requests, se usan múltiples workers.
Ejemplo: 10 workers → cada uno procesa 10K.
✔ 2. DynamoDB con transacciones
DynamoDB permite garantizar unicidad con TransactWriteItems, sin necesidad de leer antes.
Una transacción solo se ejecuta si el alias no existe.
Esto elimina la verificación redundante y asegura consistencia.
✔ 3. Batches y paralelismo
Cada transacción admite hasta 25 inserciones.
Cada worker procesa cientos de lotes en paralelo hasta completar su parte.
✔ 4. Persistencia temporal (Redis)
Si un worker falla, no se pierde el progreso.
Redis almacena los lotes pendientes para retry.
✅ Resultado:
- Funciona a 100K req/s
- Unicidad garantizada
- Bajo tiempo de respuesta
- Resistente a fallos
✅ Problema adicional: Hot Partitions
El sistema permite dominios personalizados.
Esto obliga a crear un índice secundario (GSI) por dominio en DynamoDB.
Si un cliente genera miles de URLs con el mismo dominio, todas caen en una misma partición del GSI → se satura.
✔ Solución
Agregar un sufijo a la clave del dominio:
https://itnext.io/distributed-tinyurl-architecture-how-to-handle-100k-urls-per-second-54182403117e