The current per-key and per-IP rate limiters use sync.Map + golang.org/x/time/rate (token buckets in process memory). This works for a single instance but breaks under horizontal scaling — each replica has its own state, so a client can multiply their effective rate by the number of replicas.
When to do this: when deploying more than one API instance. Not needed before that.
Suggested approach
- Replace the limiter backend with a Redis sliding-window or token-bucket implementation (e.g.
go-redis + a Lua script or a library like redis_rate)
- The
RateLimiter abstraction in internal/middleware already isolates the implementation — the swap should be localized there
- Keep the in-memory path available for local dev (env flag:
RATE_LIMITER_BACKEND=memory|redis)
- Adds Redis as a runtime dependency — update
docker-compose.yml and .env.example
The current per-key and per-IP rate limiters use
sync.Map+golang.org/x/time/rate(token buckets in process memory). This works for a single instance but breaks under horizontal scaling — each replica has its own state, so a client can multiply their effective rate by the number of replicas.When to do this: when deploying more than one API instance. Not needed before that.
Suggested approach
go-redis+ a Lua script or a library likeredis_rate)RateLimiterabstraction ininternal/middlewarealready isolates the implementation — the swap should be localized thereRATE_LIMITER_BACKEND=memory|redis)docker-compose.ymland.env.example