rrl/README.md
2020-11-19 15:47:54 -05:00

91 lines
3.6 KiB
Markdown

# rrl - Redis Rate Limiting
Very simple reusable redis-backed rate limiting code. Designed for OpenStates.org.
![Test](https://github.com/jamesturk/rrl/workflows/Test/badge.svg)
## Configuration
`rrl`, as the name implies, requires a [Redis](https://redis.io) server.
`rrl` is entirely configured by environment variables:
* **RRL_REDIS_HOST** - hostname of Redis instance (default: *localhost*)
* **RRL_REDIS_PORT** - port of Redis instance (default: *6379*)
* **RRL_REDIS_DB** - database ID to use for RRL (default: *0*)
## Usage
Usage can be throttled on a per-minute, per-hour, and/or per-day basis.
`rrl` has the concept of a `Tier` which associates a name with a set of limitations, for instance:
```
# create two tiers
bronze = Tier(name="bronze", per_minute=1, per_hour=0, per_day=500)
silver = Tier(name="silver", per_minute=5, per_hour=0, per_day=4000)
```
These tiers do not use the per_hour feature, but will limit users to 1 or 5 requests per minute respectively. There's also a daily limit at 500 or 4000 requests per day.
Then you'll need an instance of `rrl.RateLimiter`, which will be instantiated with these tiers:
```
limiter = RateLimiter(tiers=[bronze, silver])
```
Then to apply limiting, you'll call the `check_limit` function, which takes three parameters:
* `key` - A unique-per user key, often the user's API key or username. (Note: `rrl` does not know if a key is valid or not, that validation should be in your application and usually occur before the call to `check_limit`.)
* `tier_name` - The name of one of the tiers as specified when instantiating the `RateLimiter` class. (Note: `rrl` does not have a concept of which users are in which tier, that logic should be handled by your key validation code.)
Example call:
```
limiter.check_limit(key="1234", tier_name="bronze")
```
This call will return without any error if the call is deemed allowed.
If any of the rate limits are exceeded it will instead raise a `RateLimitExceeded` exception describing which limit was exceeded.
If multiple limits were exceeded it will return the shortest limit violated.
## Advanced Usage
### Obtaining Usage Information
Your `RateLimiter` instance also has a method named `get_usage_since`, which takes four parameters:
* `key` - Which key you're requesting usage information for.
* `start` - Date that you'd like usage since, as a `datetime.date` object.
* `end` - Optional end date if you'd only like usage within a certain window, otherwise the current day is used.
This will return a list of `DailyUsage` dataclasses with the following attributes:
* `date` - `datetime.date`
* `calls` - Number of calls made on that date.
This method can be useful for showing users an overview of their data.
### Advanced Configuration
When instantiating a `RateLimiter` there are several keyword-only parameters you may set:
**prefix**
Passing a prefix like:
```
limiter = RateLimiter(tiers, prefix="v1")
```
will scope all calls to limiter to a given prefix, this can be useful if you want multiple limiters but want to ensure that they do not interfere with one another.
**use_redis_time**
`True` by default, but if you set to `False` the application's system time will be used instead.
The tradeoff here is one fewer call to Redis per call to `check_limit`, but if your machines experience any clock drift unexpected results may occur.
**track_daily_usage**
`True` by default, but if set to `False`, `rrl` will not store the necessary information to make `get_usage_since` work. This results in a slight overhead reduction, but the usage information will not be stored in Redis and will be impossible to retrieve.