Files

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_api y 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/rate pero 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, RateLimitResult en functions/infra/ con .md en types/infra/
  • 1.2 rate_limiter_new — inicializa RateLimiter con rate y burst
  • 1.3 rate_limiter_check — evalua token bucket para una key, retorna RateLimitResult
  • 1.4 rate_limit_headers — construye X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After a partir de RateLimitResult
  • 1.5 rate_limiter_cleanup — goroutine que borra entries sin actividad reciente, retorna func() para cancelar

Fase 2: Middlewares + tests

  • 2.1 rate_limit_middleware — middleware que limita por IP del cliente (extrae de RemoteAddr / X-Forwarded-For)
  • 2.2 rate_limiter_by_key — middleware configurable con keyFunc para limitar por API key, user ID, etc.
  • 2.3 Tests de cada funcion con httptest.NewRecorder
  • 2.4 fn index y verificar con fn 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 + map es 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_headers como 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_cleanup que 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-For puede ser spoofed. Para uso interno es aceptable; para exposicion publica real habria que validar el header contra trusted proxies.