How sujaydevx/distributed-rate-limiter-service works
Purpose and runtime
A Spring Boot Java service implementing distributed, per-client API rate limiting. It runs as an HTTP service, uses Redis as shared mutable state across instances, and exposes Prometheus-compatible metrics. Application startup is src/main/java/com/sujay/ratelimiter/RateLimiterApplication.java; runtime configuration is in src/main/resources/application.yml.
HTTP interception boundary
The servlet filter is the enforcement point: src/main/java/com/sujay/ratelimiter/filter/RateLimitFilter.java. It executes before controllers, derives a client identifier from X-Client-Id (test override), X-Forwarded-For, or source IP, then either:
• permits the request into controller handling, or
• terminates it with HTTP 429, a Retry-After header, and a structured error response.
Controllers are therefore downstream consumers of an already-enforced request path: src/main/java/com/sujay/ratelimiter/controller/RateLimitController.java and src/main/java/com/sujay/ratelimiter/controller/DemoController.java.
Algorithm selection and contract
src/main/java/com/sujay/ratelimiter/service/RateLimiterSelector.java selects the configured implementation, notably token bucket or fixed window. src/main/java/com/sujay/ratelimiter/service/RateLimiterService.java is the common service boundary between the filter and algorithms. Decision results, remaining quota, and retry timing are carried by src/main/java/com/sujay/ratelimiter/model/RateLimitResult.java and related response/info models.
This separation allows algorithm replacement through configuration without changing the HTTP filter or controllers.
Redis-backed enforcement algorithms
src/main/java/com/sujay/ratelimiter/service/FixedWindowRateLimiter.java uses Redis atomic increment semantics for counters scoped by client and time window. It is simple but permits boundary bursts.
src/main/java/com/sujay/ratelimiter/service/TokenBucketRateLimiter.java supports burst capacity plus continuous refill. Its state transition is delegated to Redis Lua to atomically read, refill, consume, and persist bucket state:
• src/main/resources/scripts/token_bucket.lua
• src/main/resources/scripts/sliding_window.lua
src/main/java/com/sujay/ratelimiter/service/SlidingWindowRateLimiter.java is also present as an algorithm component, though the documented configured choices are token bucket and fixed window.
Failure policy and configuration
Redis connectivity and script/template setup are centralized in src/main/java/com/sujay/ratelimiter/config/RedisConfig.java. Rate limits and algorithm selection are bound through src/main/java/com/sujay/ratelimiter/config/RateLimiterProperties.java.
Algorithms handle Redis exceptions with fail-open behavior: requests proceed when Redis is unavailable, favoring API availability over strict quota enforcement. A circuit-breaker metrics configuration exists at src/main/java/com/sujay/ratelimiter/config/CircuitBreakerMetricsConfig.java, but the README identifies active circuit breaking as future scope.
Observability and local infrastructure
Each allow/deny decision is emitted as Prometheus metric ratelimiter_decisions_total, tagged by algorithm and decision. Local dependencies are orchestrated in infra/docker-compose.yml: Redis, Prometheus (infra/prometheus.yml), and Grafana with a provisioned Prometheus datasource (infra/grafana/datasources/prometheus.yml). Load generation targets the HTTP boundary via infra/k6/load-test.js.
Main entry points: RateLimitFilter.java Rate limit filter · RateLimiterService.java Rate limiter contract · docker-compose.yml Local stack · TokenBucketRateLimiter.java Token bucket
Components 20
HTTP enforcement
- Spring Boot application
src/main/java/com/sujay/ratelimiter/RateLimiterApplication.java - Rate limit filter · Derives clients and allows requests or returns 429 with Retry-After.
src/main/java/com/sujay/ratelimiter/filter/RateLimitFilter.java - Controllers · Receive only requests permitted by the filter.
src/main/java/com/sujay/ratelimiter/controller/RateLimitController.java - Demo controller
src/main/java/com/sujay/ratelimiter/controller/DemoController.java
Rate-limit algorithms
- Algorithm selector
src/main/java/com/sujay/ratelimiter/service/RateLimiterSelector.java - Rate limiter contract · Common boundary between HTTP enforcement and algorithms.
src/main/java/com/sujay/ratelimiter/service/RateLimiterService.java - Decision result · Carries allow/deny, remaining quota, and retry timing.
src/main/java/com/sujay/ratelimiter/model/RateLimitResult.java - Token bucket · Supports burst capacity and continuous refill.
src/main/java/com/sujay/ratelimiter/service/TokenBucketRateLimiter.java - Fixed window · Uses atomic counters; permits boundary bursts.
src/main/java/com/sujay/ratelimiter/service/FixedWindowRateLimiter.java - Sliding window
src/main/java/com/sujay/ratelimiter/service/SlidingWindowRateLimiter.java
State and observability
- Rate-limit properties
src/main/java/com/sujay/ratelimiter/config/RateLimiterProperties.java - Redis configuration · Centralizes connectivity and script/template setup.
src/main/java/com/sujay/ratelimiter/config/RedisConfig.java - Redis · Shared mutable state across service instances.
- Token bucket Lua · Atomically refills, consumes, and persists bucket state.
src/main/resources/scripts/token_bucket.lua - Sliding window Lua
src/main/resources/scripts/sliding_window.lua - Decision metrics · Emits ratelimiter_decisions_total tagged by algorithm and decision.
Local infrastructure
- Prometheus
infra/prometheus.yml - Grafana
infra/grafana/datasources/prometheus.yml - Local stack · Orchestrates Redis, Prometheus, and Grafana.
infra/docker-compose.yml - k6 load test · Targets the HTTP enforcement boundary.
infra/k6/load-test.js
Connections 25
- Spring Boot application → Rate limit filter: starts
- Rate limit filter → Algorithm selector: checks quota
- Rate limit filter → Controllers: allows
- Rate limit filter → Demo controller: allows
- Algorithm selector → Rate limiter contract: selects implementation
- Rate-limit properties → Algorithm selector: chooses algorithm
- Rate limiter contract → Token bucket: implementation
- Rate limiter contract → Fixed window: implementation
- Rate limiter contract → Sliding window: implementation
- Token bucket → Decision result: returns
- Fixed window → Decision result: returns
- Sliding window → Decision result: returns
- Token bucket → Token bucket Lua: executes
- Sliding window → Sliding window Lua: executes
- Token bucket Lua → Redis: atomic state transition
- Sliding window Lua → Redis: atomic state transition
- Fixed window → Redis: atomic increments
- Redis configuration → Redis: connects to
- Rate limit filter → Decision metrics: emits decisions
- Prometheus → Decision metrics: scrapes
- Grafana → Prometheus: queries
- Local stack → Redis: runs
- Local stack → Prometheus: runs
- Local stack → Grafana: runs
- k6 load test → Rate limit filter: sends HTTP traffic