Rate Limiting¶
taskito uses a token bucket algorithm to limit how fast tasks execute. Rate limits are per-task and persisted in SQLite.
Usage¶
@queue.task(rate_limit="100/m") # 100 per minute
def send_email(to, subject, body):
...
@queue.task(rate_limit="10/s") # 10 per second
def api_call(endpoint):
...
@queue.task(rate_limit="3600/h") # 3600 per hour
def generate_report(report_id):
...
Syntax¶
Rate limits use the format count/period:
| Format | Meaning |
|---|---|
"10/s" |
10 per second |
"100/m" |
100 per minute |
"3600/h" |
3600 per hour |
How It Works¶
The token bucket algorithm:
- Each task name has a bucket with
max_tokens = countand arefill_rate = count / period - Before dispatching a job, the scheduler checks if a token is available
- If a token is available, it's consumed and the job is dispatched
- If no tokens are available, the job is rescheduled 1 second in the future
Rate limit state is persisted
Token bucket state (current tokens, last refill time) is stored in the rate_limits SQLite table. This means rate limits survive worker restarts.
Per-Task, Not Per-Queue¶
Rate limits apply to the task name, regardless of which queue the job is in:
@queue.task(rate_limit="10/s", queue="emails")
def send_email(to, subject, body):
...
# Both of these are rate-limited together (same task name)
send_email.delay("alice@example.com", "Hi", "Body")
send_email.apply_async(args=("bob@example.com", "Hi", "Body"), queue="urgent")
Combining with Retries¶
Rate limiting and retries work together seamlessly. If a rate-limited task fails and retries, the retry attempt is also subject to the rate limit: