Skip to content

Commit 1b0a136

Browse files
authored
Retries on 502
Implements retries with ascending backoff on 502 errors.
1 parent abeb7d6 commit 1b0a136

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

shotgun_api3/shotgun.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,17 +3322,39 @@ def _call_rpc(self, method, params, include_auth_params=True, first=False):
33223322
if self.config.localized is True:
33233323
req_headers["locale"] = "auto"
33243324

3325-
http_status, resp_headers, body = self._make_call("POST", self.config.api_path,
3326-
encoded_payload, req_headers)
3327-
LOG.debug("Completed rpc call to %s" % (method))
3328-
try:
3329-
self._parse_http_status(http_status)
3330-
except ProtocolError as e:
3331-
e.headers = resp_headers
3332-
# 403 is returned with custom error page when api access is blocked
3333-
if e.errcode == 403:
3334-
e.errmsg += ": %s" % body
3335-
raise
3325+
attempt = 1
3326+
max_attempts = 4 # Three retries on failure
3327+
backoff = 0.75 # Seconds to wait before retry, times the attempt number
3328+
3329+
while attempt <= max_attempts:
3330+
http_status, resp_headers, body = self._make_call(
3331+
"POST",
3332+
self.config.api_path,
3333+
encoded_payload,
3334+
req_headers,
3335+
)
3336+
3337+
LOG.debug("Completed rpc call to %s" % (method))
3338+
3339+
try:
3340+
self._parse_http_status(http_status)
3341+
except ProtocolError as e:
3342+
e.headers = resp_headers
3343+
3344+
# We've seen some rare instances of SG returning 502 for issues that
3345+
# appear to be caused by something internal to SG. We're going to
3346+
# allow for limited retries for those specifically.
3347+
if attempt != max_attempts and e.errcode == 502:
3348+
LOG.debug("Got a 502 response. Waiting and retrying...")
3349+
time.sleep(float(attempt) * backoff)
3350+
attempt += 1
3351+
continue
3352+
elif e.errcode == 403:
3353+
# 403 is returned with custom error page when api access is blocked
3354+
e.errmsg += ": %s" % body
3355+
raise
3356+
else:
3357+
break
33363358

33373359
response = self._decode_response(resp_headers, body)
33383360
self._response_errors(response)

tests/test_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,13 @@ def test_call_rpc(self):
371371
expected = "rpc response with list result"
372372
self.assertEqual(d["results"], rv, expected)
373373

374+
# Test that we raise on a 502. This is ensuring the retries behavior
375+
# in place specific to 502 responses still eventually ends up raising.
376+
d = {"results": ["foo", "bar"]}
377+
a = {"some": "args"}
378+
self._mock_http(d, status=(502, "bad gateway"))
379+
self.assertRaises(api.ProtocolError, self.sg._call_rpc, "list", a)
380+
374381
def test_transform_data(self):
375382
"""Outbound data is transformed"""
376383
timestamp = time.time()

0 commit comments

Comments
 (0)