API con Límite de Tasa
Límite de tasa por usuario usando INCR + EXPIRE atómicos en nyx-kv. La primera solicitud establece un TTL de 60 segundos; las solicitudes siguientes incrementan un contador. Cuando el conteo supera el límite, se retorna 429 con el header Retry-After.
Código
// Rate-limited API — token bucket per user in nyx-kv
import "std/http"
import "std/web"
fn resp_cmd(parts: Array) -> String {
var sb: StringBuilder = StringBuilder.new()
sb.append("*" + int_to_string(parts.length()) + "\r\n")
var i: int = 0
while i < parts.length() {
let p: String = parts[i]
sb.append("$" + int_to_string(p.length()) + "\r\n" + p + "\r\n")
i = i + 1
}
return sb.to_string()
}
// Check if user has remaining quota, decrement if allowed
// Uses INCR + EXPIRE for atomic per-window counting
fn rate_check(user_id: String, max_per_minute: int) -> bool {
let fd: int = tcp_connect("127.0.0.1", 6380)
if fd < 0 { return false }
let key: String = "rate:" + user_id
// Atomic increment
tcp_write(fd, resp_cmd(["INCR", key]))
let reply: String = tcp_read_line(fd)
let count: int = string_to_int(reply.substring(1, reply.length()).trim())
// Set 60s expiration on first request
if count == 1 {
tcp_write(fd, resp_cmd(["EXPIRE", key, "60"]))
tcp_read_line(fd)
}
tcp_close(fd)
return count <= max_per_minute
}
fn api_handler(req: Request) -> Response {
let user: String = req.headers_flat[0] // in real code: extract from JWT
let allowed: bool = rate_check(user, 60)
if not allowed {
let hdrs: Array = ["Retry-After", "60"]
return Response { status: 429, headers_flat: hdrs, body: "{\"error\":\"rate limit exceeded\"}" }
}
return response_json(200, "{\"data\": \"result\"}")
}
fn main() -> int {
let app: App = app_new()
app_get(app, "/api/data", api_handler)
print("rate-limited API: 60 req/min per user")
print("state stored in nyx-kv with 60s TTL")
print("atomic INCR ensures no race conditions under concurrency")
return 0
}
Salida
rate-limited API: 60 req/min per user state stored in nyx-kv with 60s TTL atomic INCR ensures no race conditions under concurrency
Explicación
Este es el algoritmo de contador de ventana fija y compila a dos idas y vueltas con nyx-kv por solicitud. INCR es atómico — un millón de solicitudes concurrentes seguirán viendo cada una un conteo distinto — de modo que no hay condición de carrera donde dos threads crean tener el último cupo. EXPIRE se ejecuta solo cuando count == 1, creando una ventana nueva de 60 segundos. Cuando el conteo supera el límite, se retorna HTTP 429 Too Many Requests con Retry-After: 60 para que los clientes sepan exactamente cuándo reintentar. Bajo carga, el overhead es de menos de un milisegundo.