|
| 1 | +""" |
| 2 | +NS1 rate limits via a "token bucket" scheme, and provides information about |
| 3 | +rate limiting in headers on the response. Token bucket can be thought of as an |
| 4 | +initially "full" bucket, where, if not full, tokens are replenished at some |
| 5 | +rate. This allows "bursting" requests until the bucket is empty, after which, |
| 6 | +you are limited to the rate of token replenishment. |
| 7 | +
|
| 8 | +Here we define a few "strategies" that may be helpful in avoiding 429 responses |
| 9 | +from the API. |
| 10 | +
|
| 11 | +Unfortunately, rate limiting is seperately "bucketed" per endpoint and method, |
| 12 | +and are not necessarily the same for all users. So the only way to know the |
| 13 | +status of a "bucket" is to make a request, and, for now, the strategies involve |
| 14 | +sleeping for some interval *after* we make requests. They are also not |
| 15 | +currently "bucket-aware". |
| 16 | +""" |
| 17 | + |
| 18 | +import logging |
| 19 | + |
1 | 20 | from time import sleep |
2 | 21 |
|
| 22 | +LOG = logging.getLogger(__name__) |
| 23 | + |
3 | 24 |
|
4 | 25 | def rate_limit_strategy_solo(): |
5 | 26 | """ |
6 | | - sleep longer the closer we are to running out of tokens, but be blissfully |
| 27 | + Sleep longer the closer we are to running out of tokens, but be blissfully |
7 | 28 | unaware of anything else using up tokens. |
8 | 29 | """ |
9 | 30 |
|
10 | 31 | def solo_rate_limit_func(rl): |
11 | 32 | if rl["remaining"] < 2: |
12 | | - sleep(rl["period"]) |
| 33 | + wait = rl["period"] |
13 | 34 | else: |
14 | | - sleep(rl["period"] / rl["remaining"]) |
| 35 | + wait = rl["period"] / rl["remaining"] |
| 36 | + LOG.debug("rate_limit_strategy_solo: sleeping for: {}s".format(wait)) |
| 37 | + sleep(wait) |
15 | 38 |
|
16 | 39 | return solo_rate_limit_func |
17 | 40 |
|
18 | 41 |
|
19 | 42 | def rate_limit_strategy_concurrent(parallelism): |
20 | 43 | """ |
21 | | - when we have equal or fewer tokens than workers, multiply our sleep |
22 | | - interval by the number of workers. |
| 44 | + When we have equal or fewer tokens than workers, sleep for |
| 45 | + the token replenishment interval multiplied by the number of workers. |
| 46 | +
|
| 47 | + For example, if we can make 10 requests in 60 seconds, a token is |
| 48 | + replenished every 6 seconds. If parallelism is 3, we will burst 7 requests, |
| 49 | + and subsequently each process will sleep for 18 seconds before making |
| 50 | + another request. |
23 | 51 | """ |
24 | 52 |
|
25 | 53 | def concurrent_rate_limit_func(rl): |
26 | 54 | if rl["remaining"] <= parallelism: |
27 | | - sleep((rl["period"] / rl["limit"]) * parallelism) |
| 55 | + wait = (rl["period"] / rl["limit"]) * parallelism |
| 56 | + LOG.debug( |
| 57 | + "rate_limit_strategy_concurrent={}: sleeping for: {}s".format( |
| 58 | + parallelism, wait |
| 59 | + ) |
| 60 | + ) |
| 61 | + sleep(wait) |
28 | 62 |
|
29 | 63 | return concurrent_rate_limit_func |
30 | 64 |
|
|
0 commit comments