Dragonfly Cloud is now available in the AWS Marketplace - learn more

Redis Sorted Set: Rate Limit (Detailed Guide w/ Code Examples)

Use Case(s)

Redis Sorted Sets can be effectively used to implement rate limiting by keeping track of timestamps of actions performed by a user. This ensures that users do not exceed a predefined number of actions in a given time frame.

Code Examples

Python Example

import redis import time client = redis.StrictRedis(host='localhost', port=6379, db=0) def is_rate_limited(user_id, limit, interval): now = time.time() key = f"rate_limit:{user_id}" # Remove old entries client.zremrangebyscore(key, '-inf', now - interval) # Add new entry client.zadd(key, {now: now}) # Check the count count = client.zcard(key) if count > limit: return True return False # Usage example user_id = 12345 if is_rate_limited(user_id, 5, 60): # 5 actions per minute allowed print("Rate limit exceeded") else: print("Action allowed")

Node.js Example

const redis = require('redis'); const client = redis.createClient(); function isRateLimited(userId, limit, interval, callback) { const now = Date.now(); const key = `rate_limit:${userId}`; client.zremrangebyscore(key, '-inf', now - interval, () => { client.zadd(key, now, now, () => { client.zcard(key, (err, count) => { if (count > limit) { callback(true); } else { callback(false); } }); }); }); } // Usage example const userId = 12345; isRateLimited(userId, 5, 60000, (isLimited) => { if (isLimited) { console.log("Rate limit exceeded"); } else { console.log("Action allowed"); } });

Go Example

package main import ( "fmt" "github.com/go-redis/redis/v8" "context" "time" ) var ctx = context.Background() func isRateLimited(client *redis.Client, userID string, limit int64, interval time.Duration) (bool, error) { now := time.Now().Unix() key := fmt.Sprintf("rate_limit:%s", userID) // Remove old entries err := client.ZRemRangeByScore(ctx, key, "-inf", fmt.Sprint(now-int64(interval.Seconds()))).Err() if err != nil { return false, err } // Add new entry err = client.ZAdd(ctx, key, &redis.Z{Score: float64(now), Member: now}).Err() if err != nil { return false, err } // Check the count count, err := client.ZCard(ctx, key).Result() if err != nil { return false, err } return count > limit, nil } func main() { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) userID := "12345" limited, err := isRateLimited(client, userID, 5, 60*time.Second) if err != nil { panic(err) } if limited { fmt.Println("Rate limit exceeded") } else { fmt.Println("Action allowed") } }

Best Practices

  • Ensure that old entries are regularly cleaned up using ZREMRANGEBYSCORE to avoid memory bloat.
  • Use appropriate expirations for keys if they only need to exist temporarily.
  • Monitor and handle Redis connection errors gracefully to maintain application stability.

Common Mistakes

  • Forgetting to clean up old entries which can lead to incorrect rate limit calculations and increased memory usage.
  • Not handling edge cases where multiple actions happen at virtually the same timestamp, potentially causing unexpected rate limits.

FAQs

Q: How does Redis handle concurrency for rate limiting? A: Redis operations like ZADD and ZCARD are atomic, which makes rate limiting safe even under high concurrency.

Q: Can I use this method for more complex rate limiting rules? A: Yes, you can customize the logic to handle more sophisticated rules, such as different limits for different actions or adaptive rate limiting based on user behavior.

Was this content helpful?

Switch & save up to 80% 

Dragonfly is fully compatible with the Redis ecosystem and requires no code changes to implement. Instantly experience up to a 25X boost in performance and 80% reduction in cost