Skip to content

Instantly share code, notes, and snippets.

Scaling your API with rate limiters

The following are examples of the four types rate limiters discussed in the accompanying blog post. In the examples below I've used pseudocode-like Ruby, so if you're unfamiliar with Ruby you should be able to easily translate this approach to other languages. Complete examples in Ruby are also provided later in this gist.

In most cases you'll want all these examples to be classes, but I've used simple functions here to keep the code samples brief.

Request rate limiter

This uses a basic token bucket algorithm and relies on the fact that Redis scripts execute atomically. No other operations can run between fetching the count and writing the new count.

@zaharinea
zaharinea / objectid_to_uuid.py
Created January 18, 2020 18:14 — forked from enaeseth/objectid_to_uuid.py
Convert a MongoDB ObjectID to a valid, semantically similar UUID.
"""
Convert a MongoDB ObjectID to a version-1 UUID.
Python 2.7+ required for datetime.timedelta.total_seconds().
ObjectID:
- UNIX timestamp (32 bits)
- Machine identifier (24 bits)
- Process ID (16 bits)
- Counter (24 bits)
@zaharinea
zaharinea / gcra.py
Created September 16, 2019 04:02 — forked from pgjones/gcra.py
Generic Cell Rate Algorithm example
class RateLimit:
def __init__(self, count: int, period: timedelta) -> None:
self.count = count
self.period = period
@property
def inverse(self) -> float:
return self.period.total_seconds() / self.count
@zaharinea
zaharinea / shielded.py
Created August 7, 2019 20:54 — forked from romuald/shielded.py
python asyncio shield decorator
def shielded(func):
"""
Makes so an awaitable method is always shielded from cancellation
"""
@wraps(func)
async def wrapped(*args, **kwargs):
return await asyncio.shield(func(*args, **kwargs))
return wrapped
@zaharinea
zaharinea / svg2png.py
Created August 1, 2019 11:20 — forked from thomir/svg2png.py
Various ways to convert SVG -> PNG
#!/usr/bin/env python
"""Convert an SVG file to a PNG file."""
from argparse import ArgumentParser
import subprocess
import os.path
def main():
@zaharinea
zaharinea / docker-compose.yml
Created November 26, 2018 07:57 — forked from kenanscott/docker-compose.yml
Docker Compose configuration for Graylog
version: '2'
services:
# MongoDB: https://hub.docker.com/_/mongo/
mongo:
image: mongo:3
# Persistent Logs
volumes:
- mongo_data:/data/db
# Elasticsearch: https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docker.html
elasticsearch:
@zaharinea
zaharinea / graylog2-docker-compose.yml
Created November 26, 2018 07:02 — forked from jalogisch/graylog2-docker-compose.yml
Graylog2 Docker Compose file
version: '2'
networks:
graylog.net:
volumes:
graylog.data.elastic:
driver: "local"
graylog.data.mongo:
driver: "local"
@zaharinea
zaharinea / leaky_bucket.lua
Created October 9, 2018 20:10 — forked from florentchauveau/leaky_bucket.lua
Redis script (Lua) to implement a leaky bucket
-- Redis script to implement a leaky bucket
-- see https://medium.com/callr-techblog/rate-limiting-for-distributed-systems-with-redis-and-lua-eeea745cb260
-- (c) Florent CHAUVEAU <florent.chauveau@gmail.com>
local ts = tonumber(ARGV[1])
local cps = tonumber(ARGV[2])
local key = KEYS[1]
-- remove tokens < min (older than now() -1s)
local min = ts -1
@zaharinea
zaharinea / bucket_add.lua
Created October 9, 2018 20:09 — forked from florentchauveau/bucket_add.lua
Redis script (Lua) to add an event to a token bucket
-- Redis script to add an event to a token bucket
-- see https://medium.com/callr-techblog/rate-limiting-for-distributed-systems-with-redis-and-lua-eeea745cb260
-- (c) Florent CHAUVEAU <florent.chauveau@gmail.com>
local ts = tonumber(ARGV[1])
-- set the token bucket to 1 second (rolling)
local min = ts -1
-- iterate overs keys
-- Redis script to get the size of a token bucket
-- see https://medium.com/callr-techblog/rate-limiting-for-distributed-systems-with-redis-and-lua-eeea745cb260
-- (c) Florent CHAUVEAU <florent.chauveau@gmail.com>
local ts = tonumber(ARGV[1])
local key = KEYS[1]
-- remove tokens < min (older than now() -1s)
local min = ts -1