AkshadipsinhBackend · Distributed Systems
All projects
03Jan. 2025 – Mar. 2025Completed

FlowGuard

Distributed Rate Limiter

A Redis-backed rate limiter that smooths bursty traffic with a sliding-window weighted average and enforces limits before a request ever reaches application logic.

Java 21Spring Boot 3Redis
requestsFILTERhighestprecedenceRedis windowprevcurrw-avg/ minute5 min key expiry200429
HTTP 429
returned on limit violation
60s
weighted sliding window
5 min
automatic key expiry
2–3
Redis ops per request
01

Overview

FlowGuard sits in front of a Spring Boot service as a servlet filter. It tracks request counts per client IP across the current and previous minute, computes a weighted average, and rejects requests that would exceed the configured limit.

02

The problem

A naive fixed-window limiter allows double the traffic across a window boundary and blocks legitimate users with hard resets. Storing counters in a single process also breaks the moment the service scales horizontally.

FlowGuard solves both by keeping shared, self-expiring state in Redis and by weighting the two adjacent windows.

03

Architecture

Request interception
Rate limit evaluation
Shared state
Response handling

Request interception

A servlet filter with highest-precedence ordering runs on every request before routing, so no endpoint can bypass the limit.

Rate limit evaluation

The service reads the current and previous window counters from Redis and applies the weighted-average formula to decide whether the request is allowed.

Shared state

Redis holds per-IP counters, making the limiter distributed by default and consistent across instances.

Response handling

Allowed requests continue normally; violations get an immediate HTTP 429 with a clear message, kept separate from application errors.

04

Design decisions

01

Sliding-window weighted average

weighted_avg = (elapsed × current + (60 − elapsed) × previous) / 60 blends the current and previous minute, so the limit transitions smoothly instead of snapping open at the minute boundary.

02

Redis-backed shared state

Counters live in Redis and are keyed per client IP, so the limit holds across every instance of the service rather than only within a single process.

03

Enforcement before application logic

A custom servlet filter registered with highest precedence rate-checks every incoming request before it reaches a controller and returns HTTP 429 on violation.

04

Five-minute key expiry

Keys expire automatically, so stale per-IP counters never pile up and Redis memory stays bounded without a cleanup job.

05

Failure & concurrency

Burst at a minute boundary

The weighted average carries load from the previous window forward, so a client cannot double its allowance by waiting for the clock to tick.

Horizontal scaling

Because counters are centralised in Redis, adding instances does not multiply the allowed rate.

Stale clients

Keys expire after five minutes, so idle clients free their memory automatically.

Redis unavailable

The limiter's state layer is isolated behind a service boundary, keeping failure handling explicit rather than tangled with business logic.

06

Testing & verification

  • A status endpoint returns remaining requests and reset time for quick verification.
  • Rapid repeated requests against the endpoint confirm the 200 → 429 transition at the configured limit.
  • Limits and window parameters are configurable through application properties.