Nyx con Ejemplos

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.

← Anterior Siguiente →

Source: examples/by-example/99-rate-limited-api.nx