Patron de Throttling
Limita la tasa a la que un sistema procesa solicitudes o consume recursos para prevenir sobrecarga, asegurar uso justo y mantener rendimiento predecible.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Resumen
El Patron de Throttling controla la tasa a la que un sistema procesa solicitudes o consume recursos para prevenir sobrecarga y asegurar asignacion justa. En lugar de aceptar todas las solicitudes inmediatamente, el sistema limita la tasa segun capacidad, tiers de usuario o disponibilidad de recursos.
Previene fallos en cascada asegurando que servicios downstream y recursos compartidos no sean abrumados.
Cuando Usar
-
For alternatives, see Content Delivery Network (CDN) Pattern.
-
Proteger servicios downstream de picos de trafico
-
Aplicar rate limits en APIs
-
Controlar agotamiento de pool de conexiones a base de datos
-
Gestionar costos con APIs de terceros por uso
-
Asegurar asignacion justa en sistemas multi-tenant
-
Prevenir DDoS o abuso accidental
Cuando Evitar
- Servicios internos con carga predecible dentro del mismo dominio
- Sistemas donde rechazar solicitudes viola requisitos de negocio
- Cuando el cuello de botella es tamano de datos, no tasa
Solucion
Python (Token Bucket)
import time
import threading
class TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.refill_rate = refill_rate
self.tokens = capacity
self.last_refill = time.time()
self.lock = threading.Lock()
def acquire(self, tokens=1):
with self.lock:
now = time.time()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
self.last_refill = now
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
bucket = TokenBucket(capacity=10, refill_rate=2)
if not bucket.acquire():
raise Exception("Rate limit exceeded")
Java (Guava RateLimiter)
import com.google.common.util.concurrent.RateLimiter;
public class ThrottledService {
private final RateLimiter limiter = RateLimiter.create(10.0);
public String process(String request) {
limiter.acquire();
return "Processed: " + request;
}
}
JavaScript (Ventana Deslizante)
class SlidingWindowThrottle {
constructor(windowMs, maxRequests) {
this.windowMs = windowMs;
this.maxRequests = maxRequests;
this.requests = new Map();
}
isAllowed(clientId) {
const now = Date.now();
const start = now - this.windowMs;
const recent = (this.requests.get(clientId) || [])
.filter(t => t > start);
if (recent.length < this.maxRequests) {
recent.push(now);
this.requests.set(clientId, recent);
return true;
}
return false;
}
}
Explicacion
Los algoritmos de throttling balancean justicia y eficiencia:
- Token bucket: Tokens se agregan a tasa fija. Las solicitudes consumen tokens. Permite rafagas cortas manteniendo tasa promedio.
- Leaky bucket: Solicitudes entran en cola fija y gotean a tasa constante. Suaviza trafico pero descarte overflow.
- Ventana fija: Cuenta solicitudes en intervalos de tiempo. Simple pero permite rafagas en limites de ventana.
- Ventana deslizante: Mas precisa rastreando timestamps exactos dentro de una ventana rodante.
Variantes
| Variante | Comportamiento | Ideal Para |
|---|---|---|
| Token bucket | Rafagas permitidas hasta capacidad | APIs que necesitan tolerancia a rafagas |
| Leaky bucket | Tasa de salida constante | Suavizado de trafico hacia downstream |
| Ventana fija | Contador reseteado por intervalo | Implementaciones simples |
| Ventana deslizante | Ventana de tiempo rodante | Rate limits precisos por cliente |
Lo que funciona
- Retornar
429 Too Many Requestscon headerRetry-After - Diferenciar tiers de usuario con limites distintos
- Monitorear tasas de rechazo como senal temprana
- Implementar backoff para clientes que reciben throttling
- Considerar rate limiting distribuido para despliegues multi-instancia
Errores Comunes
- Throttling sin comunicar limites a clientes
- Mismos limites para todos los usuarios sin importar tier
- No manejar desviacion de reloj en sistemas distribuidos
- Olvidar limpiar entradas expiradas en algoritmos basados en ventana
Ejemplos del Mundo Real
- GitHub API: Rate limits por usuario autenticado (5000/hora) y por IP (60/hora). Exceder limites retorna
403conX-RateLimit-Reset. - AWS API Gateway: Throttling a nivel de cuenta, stage y metodo usando token bucket, con capacidad de rafaga.
Notas de Producción
- Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
- Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.
Puntos Clave
- Aplica patron de throttling cuando necesites una solución práctica para tu caso de uso.
- Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
- Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.
Temas Avanzados
Escenario: Throttling para API de Geolocalizacion
// Throttling pattern: max 10 requests por segundo
class Throttle {
private requests: number[] = [];
constructor(private maxRequests: number, private windowMs: number) {}
canProceed(): boolean {
const now = Date.now();
// Eliminar requests fuera de la ventana
this.requests = this.requests.filter(t => now - t < this.windowMs);
if (this.requests.length < this.maxRequests) {
this.requests.push(now);
return true;
}
return false;
}
timeUntilNextSlot(): number {
if (this.requests.length < this.maxRequests) return 0;
const oldest = this.requests[0];
return this.windowMs - (Date.now() - oldest);
}
}
// Uso: API de Google Maps (limit 10 req/s)
const throttle = new Throttle(10, 1000);
async function geocode(address: string): Promise<LatLng> {
if (!throttle.canProceed()) {
const wait = throttle.timeUntilNextSlot();
await new Promise(resolve => setTimeout(resolve, wait));
}
const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${address}`);
return response.json();
}
// Comparacion: Throttle vs Rate Limit vs Debounce
| Patron | Proposito | Ejemplo |
|--------|-----------|---------|
| Throttle | Max N requests por ventana | 10 req/s |
| Rate Limit | Rechazar si excede | 429 Too Many Requests |
| Debounce | Esperar a que pare el input | Search autocomplete |
| Token Bucket | Tokens se reponen over time | Burst + sustained |
| Leaky Bucket | Cola con salida constante | Suavizar picos |
Lecciones:
- Throttle limita la tasa de requests: no rechaza, espera
- Rate limit rechaza: 429 con Retry-After header
- Debounce agrupa llamadas: espera inactividad
- Token bucket permite burst: util para APIs con quotas
- Mide el throughput real: no asumas que el limite es exacto
### Como elijo entre throttle y rate limit?
Usa throttle cuando el cliente debe esperar (ej: llamar API externa con limite). Usa rate limit cuando el cliente debe ser rechazado (ej: proteger tu API de abuso). Throttle es cooperativo: el cliente se auto-limita. Rate limit es impuesto: el servidor rechaza. Para APIs publicas, usa rate limit (429 + Retry-After). Para integraciones internas, throttle es suficiente.
End of document. Review and update quarterly.
## Troubleshooting
- **Pattern does not fit the problem**: re-evaluate the forces (performance, scalability, team size, coupling). A pattern is only appropriate when its trade-offs match your constraints.
- **Too many abstractions**: if adding a pattern increases complexity without a clear benefit, simplify. Not every module needs a factory, decorator, or strategy.
- **Tight coupling after refactoring**: check that interfaces are stable and dependencies point inward.
- **Tests break when the design changes**: favor stable contracts over internal structure.
- **Performance regression from indirection**: measure before and after. Layers, decorators, and adapters can add latency; cache or inline hot paths if needed.
## Errores Comunes en Producción
- Aplicar el patrón donde no se necesita abstracción, agregando complejidad accidental.
- Dejar que el patrón se filtre en módulos no relacionados y confundir los límites de responsabilidad.
- Sobre-ingeniería en la primera implementación en lugar de comenzar simple y medir el dolor.
- Saltar los tests de contrato, de modo que las refactorizaciones rompan consumidores en silencio.
- Ignorar modos de fallo que el patrón no cubre.
- Usar el patrón como opción por defecto en lugar de elegir la herramienta adecuada para la escala actual.
- Olvidar documentar cuándo dejar de usar el patrón y qué lo reemplaza.
- Carecer de observabilidad sobre rendimiento y propagación de errores del patrón. Related Resources
Patron de Cola de Prioridad
Procesa tareas basandose en prioridad en lugar de orden de llegada, asegurando que el trabajo de alta prioridad obtenga recursos antes que las tareas de menor prioridad.
PatternPatron de Nivelacion de Carga Basada en Colas
Introduce una cola entre productores y consumidores de tareas para suavizar picos de trafico, desacoplar componentes y evitar que servicios aguas abajo sean abrumados.
PatternPatrón Circuit Breaker
Previene fallos en cascada deteniendo solicitudes a servicios que están fallando. Un patrón arquitectural para sistemas distribuidos resilientes.
Preguntas frecuentes
- ¿Es este patrón adecuado para proyectos pequeños?
- Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.
- ¿Cómo se compara este patrón con alternativas?
- Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.
- ¿Puedo aplicar este patrón parcialmente?
- Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.