Rate Limit

Walrus provides a simple RateLimit implementation that makes use of Redis’ List object to store a series of event timestamps.

As the rate-limiter logs events, it maintains a fixed-size list of timestamps. When the list of timestamps is at max capacity, Walrus will look at the difference between the oldest timestamp and the present time to determine if a new event can be logged.

Example with a rate limiter that allows 2 events every 10 seconds.

  • Log event from IP 192.168.1.2
  • List for key 192.168.1.2 now contains ['14:42:27.04521'] (these are actually unix timestamps, but are shown as times for readability).
  • Five seconds later log another event from the same IP.
  • List for 192.168.1.2 now contains ['14:42:32.08293', '14:42:27.04521']
  • Two seconds later attempt another event from the same IP. Since the list is “at capacity”, and the time difference between the oldest event and the newest is less than 10 seconds, the event will not be logged and the event will be rate-limited.

Basic usage

You can limit() to log an event and check whether it should be rate-limited:

>>> from walrus import *
>>> db = Database()
>>> rate_limit = db.rate_limit('mylimit', limit=2, per=60)  # 2 events per minute.

>>> rate_limit.limit('user-1')
False
>>> rate_limit.limit('user-1')
False
>>> rate_limit.limit('user-1')  # Slow down, user-1!
True

>>> rate_limit.limit('user-2')  # User 2 has not performed any events yet.
False

Decorator

The RateLimit.rate_limited() decorator can be used to restrict calls to a function or method. The decorator accepts a key_function parameter which instructs it how to uniquely identify the source of the function call. For example, on a web-site, you might want the key function to be derived from the requesting user’s IP address.

rate_limit = walrus.rate_limit('login-limiter', limit=3, per=60)

@app.route('/login/', methods=['GET', 'POST'])
@rate_limit.rate_limited(lambda: request.remote_addr)
def login():
    # Accept user login, etc.
    pass

Note

The rate_limited() decorator will raise a RateLimitException when an attempt to call the decorated function would exceed the allowed number of events. In your application you can catch these and perform the appropriate action.

If no key function is supplied, then Walrus will simply take the hash of all the arguments the function was called with and treat that as the key. Except for very simple functions, this is probably not what you want, so take care to ensure your key_function works as you expect.