Threshold is a highly available, load-balanced API Gateway cluster built with Express, TypeScript, and Docker. It implements a Redis-backed Token Bucket rate limiter using atomic Lua scripting and includes additional Fixed Window and Sliding Window implementations for comparison and experimentation. The mock backend service is intentionally lightweight so the project focuses entirely on gateway infrastructure rather than business logic. The system is fully instrumented with Prometheus, Grafana, k6, and structured JSON logging.
Threshold explores how production API gateways enforce distributed rate limiting while remaining observable, fault-tolerant, and horizontally scalable.
The project focuses on infrastructure and system engineering (concurrency, race condition prevention, atomic scripting, load balancing, and failure policies) rather than boilerplate CRUD application development.
- Distributed Gateway Cluster: 3 load-balanced gateway instances operating behind Nginx.
- Atomic Rate Limiting: Redis Lua-based Token Bucket engine resolving race conditions.
- Outage Resilience: Fail-open and fail-closed policies safeguarding availability/security.
- Observability Stack: Custom Prometheus scrape metrics integrated into live Grafana dashboards.
- Stress Benchmarking: Dynamic k6 tests validating request rates under high concurrency.
- Request Tracing: Structured JSON logs correlated using unique request IDs (
requestId).
graph TD
Client["Client Requests"] --> Nginx["Nginx Load Balancer (Port 80)"]
subgraph Gateway Cluster
Nginx --> Gateway1["Gateway Instance 1 (Port 3000)"]
Nginx --> Gateway2["Gateway Instance 2 (Port 3000)"]
Nginx --> Gateway3["Gateway Instance 3 (Port 3000)"]
end
Gateway1 --> Redis["Redis (Rate Limiter)"]
Gateway2 --> Redis
Gateway3 --> Redis
Gateway1 --> Backend["Backend API Service (Port 8000)"]
Gateway2 --> Backend
Gateway3 --> Backend
Prometheus["Prometheus Scraper"] --> Gateway1
Prometheus --> Gateway2
Prometheus --> Gateway3
Prometheus --> Grafana["Grafana Dashboard (Port 3001)"]
sequenceDiagram
autonumber
actor Client
participant Nginx as Nginx Load Balancer
participant Gateway as Gateway Instance
participant Redis as Redis (Lua Script)
participant Backend as Backend Service
Client->>Nginx: HTTP GET /users
Nginx->>Gateway: Forward Request (Round Robin)
rect rgb(35, 38, 46)
Note over Gateway,Redis: Rate Limiter Middleware
Gateway->>Redis: EVAL tokenBucket.lua
Note over Redis: 1. Accumulate Refilled Tokens<br/>2. Deduct Token if available<br/>3. Return Success and Retry-After
Redis-->>Gateway: Return success and retryAfter status
end
alt success is true
Gateway->>Backend: Forward Request
Backend-->>Gateway: HTTP 200 OK
Gateway-->>Client: HTTP 200 OK
else success is false
Gateway-->>Client: HTTP 429 Too Many Requests
end
- Client sends a request to the Nginx reverse proxy load balancer on port
80. - Nginx forwards the request to one of three API gateway instances using a round-robin routing policy.
- The gateway instance catches the request and executes an atomic Token Bucket Lua script inside Redis.
- If a token is available, the request is allowed and forwarded downstream to the mock backend service.
- If the bucket is empty, the gateway blocks the request and returns an HTTP
429 Too Many Requestsresponse. - Throughout the lifecycle, Prometheus scrapes gateway metrics in real-time while Grafana visualizes the charts.
- Language: TypeScript, JavaScript, Lua
- Runtime: Node.js (v24)
- Framework: Express
- Infrastructure: Docker, Docker Compose, Nginx
- Data Store: Redis
- Observability: Prometheus, Grafana
- Load Testing: k6
- Logging: Pino, Pino HTTP
The system launches as a single, containerized stack composed of the following services:
nginx: Port 80 reverse proxy round-robing requests.gateway1,gateway2,gateway3: Load balanced API gateway nodes.backend: Service endpoint containing mock resource APIs.redis: Atomic rate limiter state database.prometheus: Scraper fetching gateways operational metrics.grafana: Web console visualizing scraping reports (Port 3001).k6: Load testing utility container.
- Race Condition Prevention: Resolving read-modify-write data hazards across separate gateway instances by packaging algorithms into single atomic Redis Lua transactions.
- Fractional Token Refill: Implementing continuous token replenishment based on elapsed time in milliseconds, avoiding scheduled refill cron jobs while maintaining precision.
- Resilience Configuration: Designing a dual fail-open/fail-closed protection layer wrapped in a 250ms Redis query timeout boundary to keep gateways responsive.
- Telemetry Instrumentation: Capturing and exposing real-time HTTP metrics (latencies, counts, blocks) without introducing overhead.
- Request Correlation: Generating and propagating unique correlation IDs (
X-Request-Id) across proxies, gateway layers, and JSON logger blocks to isolate distributed traces.
threshold/
├── gateway/ # API Gateway service source code (TypeScript)
│ ├── src/
│ │ ├── lua/ # Atomic Lua scripts (tokenBucket.lua, slidingWindow.lua, etc.)
│ │ └── ...
├── backend/ # Mock Backend API service (TypeScript)
├── nginx/ # Nginx Load Balancer templates
├── prometheus/ # Prometheus scraper configurations
├── grafana/ # Grafana dashboard & datasource provisioning configs
├── k6/ # load-test.js script definitions
└── docker-compose.yml
GET /: Home endpoint returning greeting.GET /live: Liveness check returning process state.GET /ready: Readiness check evaluating active database sockets.GET /health: Evaluates Gateway connection status to dependencies.GET /metrics: Exposes Prometheus metrics output. Logs custom metrics:http_requests_total: Total count of processed requests.http_request_duration_seconds: Histogram measuring latency durations.rate_limit_blocks_total: Total count of rate-limited (HTTP 429) events.
GET /users: Returns mock user data.GET /products: Returns mock product data.
Controlled via the root .env environment configuration:
PUBLIC_PORT: Port exposed by Nginx to public clients (default:80).RATE_LIMIT_FAILURE_MODE: Define limiter outage policies (fail-openorfail-closed).GATEWAY_PORT: Port mapping internal gateway instances (default:3000).TOKEN_BUCKET_CAPACITY: Maximum burst tokens stored (default:3).TOKEN_REFILL_RATE: Time duration in seconds to refill the bucket (default:60).REDIS_TIMEOUT_MS: Milliseconds boundary before failing rate-limiting requests (default:250).
Threshold implements multiple algorithms. Here is a comparison of their trade-offs:
| Algorithm | Memory Usage | Burst Support | Accuracy | Redis Data Structure |
|---|---|---|---|---|
| Fixed Window | Low (1 key per window) | Poor (Double limits at boundaries) | Medium | String (with INCR) |
| Sliding Window | High (Keeps timestamps of all reqs) | Good (Smooth enforcement) | High | Sorted Set (ZSET) |
| Token Bucket | Low (Stores capacity, last refill epoch) | Excellent (Handles bursts cleanly) | High | Hash (HMSET) |
Under a 100 Virtual Users (VUs) concurrent stress test for 1 minute:
| Metric | Result |
|---|---|
| Throughput | 793.7 Requests/Sec (55,127 total requests) |
| Average Latency | 25.3 ms (p95: 4.22 ms) |
| Blocked Rate | 99.98% (55,121 requests rejected with 429) |
| Error Rate | 0.03% (TCP timeouts under maximum concurrency) |
Note
Understanding Latency Metrics (Why Average > p95):
Under high-concurrency rate limiting, 99.98% of requests are rejected instantly with an HTTP 429 status code (average round-trip duration: 1.5 ms). Since these fast 429 responses make up the overwhelming majority of the request volume, they dominate the percentiles, keeping the 95th percentile (p95) extremely low at 4.22 ms.
In contrast, the remaining 0.02% of requests that are allowed to pass through must be proxied downstream to the backend container, experiencing TCP queuing delays and processing overhead (average duration: 2.14 s, max: 12.81 s). These slow tail-latency outliers pull the arithmetic average up to 25.3 ms, making the average duration significantly larger than the p95 percentile.
The load test intentionally flooded the system, exceeding the configured Token Bucket rate-limit capacity. The benchmark results prove the gateway correctly and safely enforced rate limits by rejecting excess requests with HTTP 429 while keeping latencies low and minimizing internal errors.
- Docker & Docker Compose installed.
1. Spin up the entire stack:
docker compose up -d --build2. Verify container statuses:
docker compose ps3. Run a k6 load test:
docker compose run --rm k64. Shut down the stack:
docker compose down- Prometheus Setup: Prometheus Scraper Config Reference
- Grafana Provisioning: Grafana Provisioning Guide
- Grafana k6: k6 JavaScript API Reference
- Redis Scripting: Redis Lua Programmability (EVAL)
- Pino Logging: Pino API Documentation
- Docker Compose: Docker Compose Overview & CLI