Skip to content

Commit d504a5a

Browse files
SG-34197 Retry S3 uploads on error 500 (#324)
* Retry S3 uploads on error 500 * Remove context manager variable * Improve conditional logic
1 parent 37af315 commit d504a5a

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

shotgun_api3/shotgun.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4145,16 +4145,14 @@ def _upload_data_to_storage(self, data, content_type, size, storage_url):
41454145
LOG.debug("Completed request to %s" % request.get_method())
41464146

41474147
except urllib.error.HTTPError as e:
4148-
if e.code == 500:
4149-
raise ShotgunError("Server encountered an internal error.\n%s\n%s\n\n" % (storage_url, e))
4150-
elif attempt != max_attempts and e.code == 503:
4151-
LOG.debug("Got a 503 response. Waiting and retrying...")
4148+
if attempt != max_attempts and e.code in [500, 503]:
4149+
LOG.debug("Got a %s response. Waiting and retrying..." % e.code)
41524150
time.sleep(float(attempt) * backoff)
41534151
attempt += 1
41544152
continue
4153+
elif e.code in [500, 503]:
4154+
raise ShotgunError("Got a %s response when uploading to %s: %s" % (e.code, storage_url, e))
41554155
else:
4156-
if e.code == 503:
4157-
raise ShotgunError("Got a 503 response when uploading to %s: %s" % (storage_url, e))
41584156
raise ShotgunError("Unanticipated error occurred uploading to %s: %s" % (storage_url, e))
41594157

41604158
else:

tests/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def setUp(self):
119119
self._setup_mock()
120120
self._setup_mock_data()
121121

122-
def _setup_mock(self):
122+
def _setup_mock(self, s3_status_code_error=503):
123123
"""Setup mocking on the ShotgunClient to stop it calling a live server
124124
"""
125125
# Replace the function used to make the final call to the server
@@ -131,7 +131,7 @@ def _setup_mock(self):
131131
self.sg._make_upload_request = mock.Mock(spec=api.Shotgun._make_upload_request,
132132
side_effect = urllib.error.HTTPError(
133133
"url",
134-
503,
134+
s3_status_code_error,
135135
"The server is currently down or to busy to reply."
136136
"Please try again later.",
137137
{},

tests/test_client.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def test_call_rpc(self):
454454
"Call is repeated up to 3 times",
455455
)
456456

457-
def test_upload_s3(self):
457+
def test_upload_s3_503(self):
458458
"""
459459
Test 503 response is retried when uploading to S3.
460460
"""
@@ -476,6 +476,30 @@ def test_upload_s3(self):
476476
self.assertTrue(
477477
max_attempts == self.sg._make_upload_request.call_count,
478478
"Call is repeated up to 3 times")
479+
480+
def test_upload_s3_500(self):
481+
"""
482+
Test 500 response is retried when uploading to S3.
483+
"""
484+
self._setup_mock(s3_status_code_error=500)
485+
this_dir, _ = os.path.split(__file__)
486+
storage_url = "http://foo.com/"
487+
path = os.path.abspath(os.path.expanduser(
488+
os.path.join(this_dir, "sg_logo.jpg")))
489+
max_attempts = 4 # Max retries to S3 server attempts
490+
# Expected HTTPError exception error message
491+
expected = "The server is currently down or to busy to reply." \
492+
"Please try again later."
493+
494+
# Test the Internal function that is used to upload each
495+
# data part in the context of multi-part uploads to S3, we
496+
# simulate the HTTPError exception raised with 503 status errors
497+
with self.assertRaises(api.ShotgunError, msg=expected):
498+
self.sg._upload_file_to_storage(path, storage_url)
499+
# Test the max retries attempt
500+
self.assertTrue(
501+
max_attempts == self.sg._make_upload_request.call_count,
502+
"Call is repeated up to 3 times")
479503

480504
def test_transform_data(self):
481505
"""Outbound data is transformed"""

0 commit comments

Comments
 (0)