Distributed Rate Limiter
A Redis-backed sliding-window rate limiter for horizontally scaled Go services.
This project implements a sliding-window rate limiter that keeps request timestamps in Redis sorted sets, so every application instance evaluates the same shared counter. Each decision trims expired entries, counts the remaining hits inside the active window, and records the current request with a TTL that bounds storage growth.
The design is intentionally small: Redis provides atomic-ish ordering through a transaction pipeline, while Go keeps the limiter interface synchronous and easy to wrap around HTTP handlers or RPC interceptors. Denied requests remove their tentative entry after the pipeline completes, keeping the window accurate without client-side coordination.
func Allow(ctx context.Context, rdb *redis.Client, key string, limit int, window time.Duration) (bool, error) {
now := time.Now()
cutoff := now.Add(-window).UnixMilli()
member := strconv.FormatInt(now.UnixNano(), 10)
pipe := rdb.TxPipeline()
pipe.ZRemRangeByScore(ctx, key, "0", strconv.FormatInt(cutoff, 10))
count := pipe.ZCard(ctx, key)
pipe.ZAdd(ctx, key, redis.Z{Score: float64(now.UnixMilli()), Member: member})
pipe.Expire(ctx, key, window)
if _, err := pipe.Exec(ctx); err != nil {
return false, err
}
if count.Val() >= int64(limit) {
_ = rdb.ZRem(ctx, key, member).Err()
return false, nil
}
return true, nil
}