Skip to content

Commit 1271686

Browse files
feat: support configurable retries in upload_chunks_concurrently (#1120)
* feat: support configurable retries in upload_chunks_concurrently * lint * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 14a1909 commit 1271686

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

google/cloud/storage/transfer_manager.py

+22
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from google.cloud.storage import Blob
2929
from google.cloud.storage.blob import _get_host_name
3030
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
31+
from google.cloud.storage._helpers import _api_core_retry_to_resumable_media_retry
32+
from google.cloud.storage.retry import DEFAULT_RETRY
3133

3234
from google.resumable_media.requests.upload import XMLMPUContainer
3335
from google.resumable_media.requests.upload import XMLMPUPart
@@ -871,6 +873,7 @@ def upload_chunks_concurrently(
871873
*,
872874
checksum="md5",
873875
timeout=_DEFAULT_TIMEOUT,
876+
retry=DEFAULT_RETRY,
874877
):
875878
"""Upload a single file in chunks, concurrently.
876879
@@ -966,6 +969,20 @@ def upload_chunks_concurrently(
966969
(Optional) The amount of time, in seconds, to wait
967970
for the server response. See: :ref:`configuring_timeouts`
968971
972+
:type retry: google.api_core.retry.Retry
973+
:param retry: (Optional) How to retry the RPC. A None value will disable
974+
retries. A google.api_core.retry.Retry value will enable retries,
975+
and the object will configure backoff and timeout options. Custom
976+
predicates (customizable error codes) are not supported for media
977+
operations such as this one.
978+
979+
This function does not accept ConditionalRetryPolicy values because
980+
preconditions are not supported by the underlying API call.
981+
982+
See the retry.py source code and docstrings in this package
983+
(google.cloud.storage.retry) for information on retry types and how
984+
to configure them.
985+
969986
:raises: :exc:`concurrent.futures.TimeoutError` if deadline is exceeded.
970987
"""
971988

@@ -995,6 +1012,8 @@ def upload_chunks_concurrently(
9951012
headers["x-goog-encryption-kms-key-name"] = blob.kms_key_name
9961013

9971014
container = XMLMPUContainer(url, filename, headers=headers)
1015+
container._retry_strategy = _api_core_retry_to_resumable_media_retry(retry)
1016+
9981017
container.initiate(transport=transport, content_type=content_type)
9991018
upload_id = container.upload_id
10001019

@@ -1025,6 +1044,7 @@ def upload_chunks_concurrently(
10251044
part_number=part_number,
10261045
checksum=checksum,
10271046
headers=headers,
1047+
retry=retry,
10281048
)
10291049
)
10301050

@@ -1054,6 +1074,7 @@ def _upload_part(
10541074
part_number,
10551075
checksum,
10561076
headers,
1077+
retry,
10571078
):
10581079
"""Helper function that runs inside a thread or subprocess to upload a part.
10591080
@@ -1075,6 +1096,7 @@ def _upload_part(
10751096
checksum=checksum,
10761097
headers=headers,
10771098
)
1099+
part._retry_strategy = _api_core_retry_to_resumable_media_retry(retry)
10781100
part.upload(client._http)
10791101
return (part_number, part.etag)
10801102

tests/unit/test_transfer_manager.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,11 @@ def test_upload_chunks_concurrently():
658658
container_mock.register_part.assert_any_call(1, ETAG)
659659
container_mock.register_part.assert_any_call(2, ETAG)
660660
container_mock.finalize.assert_called_once_with(bucket.client._http)
661+
662+
assert container_mock._retry_strategy.max_sleep == 60.0
663+
assert container_mock._retry_strategy.max_cumulative_retry == 120.0
664+
assert container_mock._retry_strategy.max_retries is None
665+
661666
part_mock.upload.assert_called_with(transport)
662667

663668

@@ -693,12 +698,15 @@ def test_upload_chunks_concurrently_passes_concurrency_options():
693698
worker_type=transfer_manager.THREAD,
694699
max_workers=MAX_WORKERS,
695700
deadline=DEADLINE,
701+
retry=None,
696702
)
697703
except ValueError:
698704
pass # The futures don't actually work, so we expect this to abort.
699705
# Conveniently, that gives us a chance to test the auto-delete
700706
# exception handling feature.
701707
container_mock.cancel.assert_called_once_with(transport)
708+
assert container_mock._retry_strategy.max_retries == 0
709+
702710
pool_patch.assert_called_with(max_workers=MAX_WORKERS)
703711
wait_patch.assert_called_with(mock.ANY, timeout=DEADLINE, return_when=mock.ANY)
704712

@@ -905,6 +913,8 @@ def test__download_and_write_chunk_in_place():
905913

906914

907915
def test__upload_part():
916+
from google.cloud.storage.retry import DEFAULT_RETRY
917+
908918
pickled_mock = pickle.dumps(_PickleableMockClient())
909919
FILENAME = "file_a.txt"
910920
UPLOAD_ID = "abcd"
@@ -916,9 +926,22 @@ def test__upload_part():
916926
"google.cloud.storage.transfer_manager.XMLMPUPart", return_value=part
917927
):
918928
result = transfer_manager._upload_part(
919-
pickled_mock, URL, UPLOAD_ID, FILENAME, 0, 256, 1, None, {"key", "value"}
929+
pickled_mock,
930+
URL,
931+
UPLOAD_ID,
932+
FILENAME,
933+
0,
934+
256,
935+
1,
936+
None,
937+
{"key", "value"},
938+
retry=DEFAULT_RETRY,
920939
)
921940
part.upload.assert_called_once()
941+
assert part._retry_strategy.max_sleep == 60.0
942+
assert part._retry_strategy.max_cumulative_retry == 120.0
943+
assert part._retry_strategy.max_retries is None
944+
922945
assert result == (1, ETAG)
923946

924947

0 commit comments

Comments
 (0)