fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.2 KiB
7.2 KiB
id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
| id | title | status | type | domain | scope | priority | depends | blocks | related | created | updated | tags |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0016 | Rate Limiting | completado | feature | multi-app | media | 2026-05-17 | 2026-05-17 |
0016 — Rate Limiting
Metadata
| Campo | Valor |
|---|---|
| ID | 0016 |
| Estado | pendiente |
| Prioridad | media |
| Tipo | feature |
Dependencias
- 0009 (HTTP Server Foundation) — el middleware de rate limiting se integra via
http_middleware_chain.
Objetivo
Proteger cualquier API del registry contra abuso con rate limiting in-memory basado en token bucket, sin dependencias externas (no Redis). Funciones componibles que se enchufan al stack de middlewares de 0009.
Contexto
sqlite_apiy futuras apps HTTP no tienen ninguna proteccion contra abuso. Un cliente puede hacer miles de requests por segundo sin limite.- Con las funciones de HTTP server de 0009, integrar rate limiting es cuestion de componer un middleware mas en la chain.
- Token bucket es el algoritmo estandar para rate limiting HTTP: permite rafagas controladas (
burst) mientras mantiene una tasa sostenida (rate). - Go stdlib incluye
golang.org/x/time/ratepero crear funciones propias permite control total sobre cleanup, headers y key extraction.
Arquitectura
functions/infra/
rate_limiter_new.go — NEW: crea rate limiter in-memory
rate_limiter_new.md — NEW
rate_limiter_check.go — NEW: consulta si un request esta permitido
rate_limiter_check.md — NEW
rate_limit_middleware.go — NEW: middleware HTTP por IP
rate_limit_middleware.md — NEW
rate_limiter_by_key.go — NEW: rate limit por clave custom
rate_limiter_by_key.md — NEW
rate_limiter_cleanup.go — NEW: GC de entries stale
rate_limiter_cleanup.md — NEW
rate_limit_headers.go — NEW: construye headers estandar
rate_limit_headers.md — NEW
types/infra/
rate_limiter.md — NEW
rate_limit_config.md — NEW
rate_limit_result.md — NEW
Diseno
Tipos
// RateLimiter mantiene estado de todos los clientes
type RateLimiter struct {
rate float64 // tokens por segundo
burst int // capacidad maxima del bucket
mu sync.Mutex
clients map[string]*clientEntry // key -> bucket state
}
type clientEntry struct {
tokens float64
lastSeen time.Time
}
// RateLimitConfig configura el middleware
type RateLimitConfig struct {
RequestsPerSecond float64 // tasa sostenida
BurstSize int // rafaga maxima
KeyFunc func(r *http.Request) string // extractor de clave (nil = IP)
CleanupInterval time.Duration // frecuencia de GC
}
// RateLimitResult es el resultado de un check
type RateLimitResult struct {
Allowed bool // request permitido
Remaining int // tokens restantes
ResetAt time.Time // cuando se rellena el bucket
RetryAfter float64 // segundos hasta que se pueda reintentar (0 si allowed)
}
Funciones
| Funcion | Purity | Firma (simplificada) |
|---|---|---|
rate_limiter_new |
impure | (rate float64, burst int) *RateLimiter |
rate_limiter_check |
impure | (rl *RateLimiter, key string) RateLimitResult |
rate_limit_middleware |
impure | (rl *RateLimiter) Middleware |
rate_limiter_by_key |
impure | (rl *RateLimiter, keyFunc func(*http.Request) string) Middleware |
rate_limiter_cleanup |
impure | (rl *RateLimiter, maxAge time.Duration, interval time.Duration) func() |
rate_limit_headers |
pure | (result RateLimitResult, limit int) http.Header |
Token bucket
Cada key tiene un bucket con burst tokens. Se recargan a rate tokens/segundo. Un request consume 1 token. Si no quedan tokens, se rechaza con 429.
Tareas
Fase 1: Core + tipos
- 1.1 Crear tipos
RateLimiter,RateLimitConfig,RateLimitResultenfunctions/infra/con.mdentypes/infra/ - 1.2
rate_limiter_new— inicializaRateLimitercon rate y burst - 1.3
rate_limiter_check— evalua token bucket para una key, retornaRateLimitResult - 1.4
rate_limit_headers— construyeX-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset,Retry-Aftera partir deRateLimitResult - 1.5
rate_limiter_cleanup— goroutine que borra entries sin actividad reciente, retornafunc()para cancelar
Fase 2: Middlewares + tests
- 2.1
rate_limit_middleware— middleware que limita por IP del cliente (extrae deRemoteAddr/X-Forwarded-For) - 2.2
rate_limiter_by_key— middleware configurable conkeyFuncpara limitar por API key, user ID, etc. - 2.3 Tests de cada funcion con
httptest.NewRecorder - 2.4
fn indexy verificar confn show
Ejemplo de uso
// Crear limiter: 10 req/s con burst de 20
rl := infra.RateLimiterNew(10, 20)
// Arrancar cleanup cada 5 minutos, borra entries sin actividad en 10 min
stopCleanup := infra.RateLimiterCleanup(rl, 10*time.Minute, 5*time.Minute)
defer stopCleanup()
// Componer con otros middlewares de 0009
middleware := infra.HttpMiddlewareChain(
infra.HttpCorsMiddleware([]string{"*"}, []string{"GET", "POST"}),
infra.RateLimitMiddleware(rl), // por IP
infra.HttpLoggerMiddleware(os.Stdout),
)
mux := infra.HttpRouter(routes)
infra.HttpServe(":8484", middleware(mux), ctx)
// Rate limit por API key en vez de IP
keyMiddleware := infra.RateLimiterByKey(rl, func(r *http.Request) string {
return r.Header.Get("X-API-Key")
})
// Respuesta 429 automatica del middleware:
// HTTP/1.1 429 Too Many Requests
// X-RateLimit-Limit: 10
// X-RateLimit-Remaining: 0
// X-RateLimit-Reset: 1713045600
// Retry-After: 1
// Content-Type: application/json
//
// {"status":429,"code":"rate_limited","message":"too many requests"}
Decisiones de diseno
- In-memory, no Redis: para el scope del registry (single-process, pocas apps) un
sync.Mutex+mapes suficiente y evita una dependencia de infraestructura. - Token bucket sobre sliding window: permite rafagas legitimas (burst) sin penalizar al cliente por picos puntuales, y es trivial de implementar.
- Headers IETF draft: sigue
draft-ietf-httpapi-ratelimit-headers(X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset). Los clientes pueden adaptar su ritmo sin adivinar. rate_limit_headerscomo funcion pura: construir headers no requiere I/O, solo formateo. El middleware la usa internamente pero queda disponible para otros usos.- Cleanup explicito: el GC goroutine se arranca con parametros configurables y se para con la funcion retornada, sin goroutine leaks.
Riesgos
- Memoria con muchas IPs unicas: Mitigado con
rate_limiter_cleanupque purga entries inactivas periodicamente. Para APIs con millones de IPs distintas habria que migrar a Redis, pero ese no es el caso del registry. - IP detras de proxy:
X-Forwarded-Forpuede ser spoofed. Para uso interno es aceptable; para exposicion publica real habria que validar el header contra trusted proxies.