Skip to content

Commit

Permalink
Fix rate limiting backoff behavior (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlecRosenbaum authored Apr 6, 2022
1 parent 256b416 commit 8dd7fa3
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 34 deletions.
25 changes: 15 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@ workflows:
version: 2
workflow:
jobs:
- test-2.7
- test-3.5
- test-3.6
- test-3.7
- test-3.8
- test-3.9
- test-3.10

defaults: &defaults
working_directory: ~/code
steps:
- checkout
- run:
name: Install dependencies
command: sudo pip install -r requirements.txt
command: pip install -r requirements.txt
- run:
name: Test
command: pytest

jobs:
test-2.7:
test-3.7:
<<: *defaults
docker:
- image: circleci/python:2.7
test-3.5:
- image: cimg/python:3.7
test-3.8:
<<: *defaults
docker:
- image: circleci/python:3.5
test-3.6:
- image: cimg/python:3.8
test-3.9:
<<: *defaults
docker:
- image: circleci/python:3.6
- image: cimg/python:3.9
test-3.10:
<<: *defaults
docker:
- image: cimg/python:3.10
18 changes: 10 additions & 8 deletions closeio_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import logging
import time

Expand All @@ -10,8 +11,8 @@
DEFAULT_RATE_LIMIT_DELAY = 2 # Seconds

# To update the package version, change this variable. This variable is also
# read by setup.py when installing the package.
__version__ = '1.4'
# read by setup.py when installing the package.
__version__ = '2.0'

class APIError(Exception):
"""Raised when sending a request to the API failed."""
Expand Down Expand Up @@ -145,12 +146,13 @@ def _get_rate_limit_sleep_time(self, response):
"""Get rate limit window expiration time from response if the response
status code is 429.
"""
try:
data = response.json()
return float(data['error']['rate_reset'])
except (AttributeError, KeyError, ValueError):
logging.exception('Error parsing rate limiting response')
return DEFAULT_RATE_LIMIT_DELAY
with contextlib.suppress(KeyError):
return float(response.headers["Retry-After"])
with contextlib.suppress(KeyError):
return float(response.headers["RateLimit-Reset"])

logging.exception('Error parsing rate limiting response')
return DEFAULT_RATE_LIMIT_DELAY

def _get_randomized_sleep_time_for_error(self, status_code, retries):
"""Get sleep time for a given status code before we can try the
Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pytest==3.0.7
requests==2.21.0
responses==0.5.1
pytest==7.1.1
requests==2.27.1
responses==0.20.0
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
],
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Operating System :: OS Independent",
]
)
12 changes: 3 additions & 9 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,10 @@ def test_retry_on_rate_limit(api_client):
rsps.add(
responses.GET,
'https://api.close.com/api/v1/lead/lead_abcdefghijklmnop/',
body=json.dumps({
"error": {
"rate_reset": 1,
"message": "API call count exceeded for this 15 second window",
"rate_limit": 600,
"rate_window": 15
}
}),
body=json.dumps({}),
status=429,
content_type='application/json'
content_type='application/json',
headers={"Retry-After": "1", "RateLimit-Reset": "1"}
)

# Respond correctly to the second request.
Expand Down

0 comments on commit 8dd7fa3

Please sign in to comment.