Skip to content

Commit 547740c

Browse files
authored
fix: retry uploads only conditionally (#316)
* fix: retry uploads only conditionally * update docstring for num_retries
1 parent dc36719 commit 547740c

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

google/cloud/storage/blob.py

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,11 @@
103103
"release. The default behavior (when `num_retries` is not specified) when "
104104
"a transient error (e.g. 429 Too Many Requests or 500 Internal Server "
105105
"Error) occurs will be as follows: upload requests will be automatically "
106-
"retried. Subsequent retries will be sent after waiting 1, 2, 4, 8, etc. "
107-
"seconds (exponential backoff) until 10 minutes of wait time have "
108-
"elapsed. At that point, there will be no more attempts to retry."
106+
"retried if and only if `if_metageneration_match` is specified (thus "
107+
"making the upload idempotent). Subsequent retries will be sent after "
108+
"waiting 1, 2, 4, 8, etc. seconds (exponential backoff) until 10 minutes "
109+
"of wait time have elapsed. At that point, there will be no more attempts "
110+
"to retry."
109111
)
110112
_READ_LESS_THAN_SIZE = (
111113
"Size {:d} was specified but the file-like object only had " "{:d} bytes remaining."
@@ -1550,8 +1552,14 @@ def _do_multipart_upload(
15501552
concluded once ``stream`` is exhausted (or :data:`None`).
15511553
15521554
:type num_retries: int
1553-
:param num_retries: Number of upload retries. (Deprecated: This
1554-
argument will be removed in a future release.)
1555+
:param num_retries: Number of upload retries. By default, only uploads
1556+
with if_metageneration_match set will be retried, as
1557+
uploads without the argument are not guaranteed to
1558+
be idempotent. Setting num_retries will override
1559+
this default behavior and guarantee retries even
1560+
when if_metageneration_match is not set.
1561+
(Deprecated: This argument will be removed in a
1562+
future release.)
15551563
15561564
:type predefined_acl: str
15571565
:param predefined_acl: (Optional) Predefined access control list
@@ -1709,8 +1717,14 @@ def _initiate_resumable_upload(
17091717
:param predefined_acl: (Optional) Predefined access control list
17101718
17111719
:type num_retries: int
1712-
:param num_retries: Number of upload retries. (Deprecated: This
1713-
argument will be removed in a future release.)
1720+
:param num_retries: Number of upload retries. By default, only uploads
1721+
with if_metageneration_match set will be retried, as
1722+
uploads without the argument are not guaranteed to
1723+
be idempotent. Setting num_retries will override
1724+
this default behavior and guarantee retries even
1725+
when if_metageneration_match is not set.
1726+
(Deprecated: This argument will be removed in a
1727+
future release.)
17141728
17151729
:type extra_headers: dict
17161730
:param extra_headers: (Optional) Extra headers to add to standard
@@ -1887,8 +1901,14 @@ def _do_resumable_upload(
18871901
concluded once ``stream`` is exhausted (or :data:`None`).
18881902
18891903
:type num_retries: int
1890-
:param num_retries: Number of upload retries. (Deprecated: This
1891-
argument will be removed in a future release.)
1904+
:param num_retries: Number of upload retries. By default, only uploads
1905+
with if_metageneration_match set will be retried, as
1906+
uploads without the argument are not guaranteed to
1907+
be idempotent. Setting num_retries will override
1908+
this default behavior and guarantee retries even
1909+
when if_metageneration_match is not set.
1910+
(Deprecated: This argument will be removed in a
1911+
future release.)
18921912
18931913
:type predefined_acl: str
18941914
:param predefined_acl: (Optional) Predefined access control list
@@ -2005,8 +2025,14 @@ def _do_upload(
20052025
concluded once ``stream`` is exhausted (or :data:`None`).
20062026
20072027
:type num_retries: int
2008-
:param num_retries: Number of upload retries. (Deprecated: This
2009-
argument will be removed in a future release.)
2028+
:param num_retries: Number of upload retries. By default, only uploads
2029+
with if_metageneration_match set will be retried, as
2030+
uploads without the argument are not guaranteed to
2031+
be idempotent. Setting num_retries will override
2032+
this default behavior and guarantee retries even
2033+
when if_metageneration_match is not set.
2034+
(Deprecated: This argument will be removed in a
2035+
future release.)
20102036
20112037
:type predefined_acl: str
20122038
:param predefined_acl: (Optional) Predefined access control list
@@ -2058,6 +2084,15 @@ def _do_upload(
20582084
**only** response in the multipart case and it will be the
20592085
**final** response in the resumable case.
20602086
"""
2087+
if if_metageneration_match is None and num_retries is None:
2088+
# Uploads are only idempotent (safe to retry) if
2089+
# if_metageneration_match is set. If it is not set, the default
2090+
# num_retries should be 0. Note: Because retry logic for uploads is
2091+
# provided by the google-resumable-media-python package, it doesn't
2092+
# use the ConditionalRetryStrategy class used in other API calls in
2093+
# this library to solve this problem.
2094+
num_retries = 0
2095+
20612096
if size is not None and size <= _MAX_MULTIPART_SIZE:
20622097
response = self._do_multipart_upload(
20632098
client,
@@ -2161,8 +2196,14 @@ def upload_from_file(
21612196
:param content_type: (Optional) Type of content being uploaded.
21622197
21632198
:type num_retries: int
2164-
:param num_retries: Number of upload retries. (Deprecated: This
2165-
argument will be removed in a future release.)
2199+
:param num_retries: Number of upload retries. By default, only uploads
2200+
with if_metageneration_match set will be retried, as
2201+
uploads without the argument are not guaranteed to
2202+
be idempotent. Setting num_retries will override
2203+
this default behavior and guarantee retries even
2204+
when if_metageneration_match is not set.
2205+
(Deprecated: This argument will be removed in a
2206+
future release.)
21662207
21672208
:type client: :class:`~google.cloud.storage.client.Client`
21682209
:param client: (Optional) The client to use. If not passed, falls back

tests/unit/test_blob.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,6 +2577,12 @@ def _do_upload_helper(
25772577
if_metageneration_not_match,
25782578
**timeout_kwarg
25792579
)
2580+
2581+
# Adjust num_retries expectations to reflect the conditional default in
2582+
# _do_upload()
2583+
if num_retries is None and if_metageneration_match is None:
2584+
num_retries = 0
2585+
25802586
self.assertIs(created_json, mock.sentinel.json)
25812587
response.json.assert_called_once_with()
25822588
if size is not None and size <= _MAX_MULTIPART_SIZE:

0 commit comments

Comments
 (0)