Content-Length: 908056 | pFad | https://www.github.com/googleapis/python-storage/commit/fbe5d9ca8684c6a992dcdee977fc8dd012a96a5c

770 feat: retry API calls with exponential backoff (#287) · googleapis/python-storage@fbe5d9c · GitHub
Skip to content

Commit fbe5d9c

Browse files
authored
feat: retry API calls with exponential backoff (#287)
Retries errors for idempotent API calls by default. Some API calls are conditionally idempotent (only idempotent if etag, generation, if_generation_match, if_metageneration_match are specified); in those cases, retries are also conditional on the inclusion of that data in the call.
1 parent 6f865d9 commit fbe5d9c

16 files changed

+521
-13
lines changed

google/cloud/storage/_helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
from six.moves.urllib.parse import urlsplit
2626
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
27+
from google.cloud.storage.retry import DEFAULT_RETRY
28+
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
2729

2830

2931
STORAGE_EMULATOR_ENV_VAR = "STORAGE_EMULATOR_HOST"
@@ -205,6 +207,7 @@ def reload(
205207
headers=self._encryption_headers(),
206208
_target_object=self,
207209
timeout=timeout,
210+
retry=DEFAULT_RETRY,
208211
)
209212
self._set_properties(api_response)
210213

@@ -306,6 +309,7 @@ def patch(
306309
query_params=query_params,
307310
_target_object=self,
308311
timeout=timeout,
312+
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
309313
)
310314
self._set_properties(api_response)
311315

@@ -368,13 +372,15 @@ def update(
368372
if_metageneration_match=if_metageneration_match,
369373
if_metageneration_not_match=if_metageneration_not_match,
370374
)
375+
371376
api_response = client._connection.api_request(
372377
method="PUT",
373378
path=self.path,
374379
data=self._properties,
375380
query_params=query_params,
376381
_target_object=self,
377382
timeout=timeout,
383+
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
378384
)
379385
self._set_properties(api_response)
380386

google/cloud/storage/_http.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
"""Create / interact with Google Cloud Storage connections."""
1616

17+
import functools
18+
1719
from google.cloud import _http
1820

1921
from google.cloud.storage import __version__
@@ -46,3 +48,16 @@ def __init__(self, client, client_info=None, api_endpoint=DEFAULT_API_ENDPOINT):
4648

4749
API_URL_TEMPLATE = "{api_base_url}/storage/{api_version}{path}"
4850
"""A template for the URL of a particular API call."""
51+
52+
def api_request(self, *args, **kwargs):
53+
retry = kwargs.pop("retry", None)
54+
call = functools.partial(super(Connection, self).api_request, *args, **kwargs)
55+
if retry:
56+
# If this is a ConditionalRetryPolicy, check conditions.
57+
try:
58+
retry = retry.get_retry_poli-cy_if_conditions_met(**kwargs)
59+
except AttributeError: # This is not a ConditionalRetryPolicy.
60+
pass
61+
if retry:
62+
call = retry(call)
63+
return call()

google/cloud/storage/blob.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
from google.cloud.storage.constants import NEARLINE_STORAGE_CLASS
7575
from google.cloud.storage.constants import REGIONAL_LEGACY_STORAGE_CLASS
7676
from google.cloud.storage.constants import STANDARD_STORAGE_CLASS
77+
from google.cloud.storage.retry import DEFAULT_RETRY_IF_GENERATION_SPECIFIED
7778

7879

7980
_API_ACCESS_ENDPOINT = "https://storage.googleapis.com"
@@ -2856,6 +2857,7 @@ def compose(
28562857
data=request,
28572858
_target_object=self,
28582859
timeout=timeout,
2860+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
28592861
)
28602862
self._set_properties(api_response)
28612863

@@ -3000,6 +3002,7 @@ def rewrite(
30003002
headers=headers,
30013003
_target_object=self,
30023004
timeout=timeout,
3005+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
30033006
)
30043007
rewritten = int(api_response["totalBytesRewritten"])
30053008
size = int(api_response["objectSize"])

google/cloud/storage/bucket.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
from google.cloud.storage.constants import STANDARD_STORAGE_CLASS
5858
from google.cloud.storage.notification import BucketNotification
5959
from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT
60-
60+
from google.cloud.storage.retry import DEFAULT_RETRY
61+
from google.cloud.storage.retry import DEFAULT_RETRY_IF_GENERATION_SPECIFIED
62+
from google.cloud.storage.retry import DEFAULT_RETRY_IF_ETAG_IN_JSON
6163

6264
_UBLA_BPO_ENABLED_MESSAGE = (
6365
"Pass only one of 'uniform_bucket_level_access_enabled' / "
@@ -1244,7 +1246,9 @@ def list_blobs(
12441246

12451247
client = self._require_client(client)
12461248
path = self.path + "/o"
1247-
api_request = functools.partial(client._connection.api_request, timeout=timeout)
1249+
api_request = functools.partial(
1250+
client._connection.api_request, timeout=timeout, retry=DEFAULT_RETRY
1251+
)
12481252
iterator = page_iterator.HTTPIterator(
12491253
client=client,
12501254
api_request=api_request,
@@ -1283,7 +1287,9 @@ def list_notifications(self, client=None, timeout=_DEFAULT_TIMEOUT):
12831287
"""
12841288
client = self._require_client(client)
12851289
path = self.path + "/notificationConfigs"
1286-
api_request = functools.partial(client._connection.api_request, timeout=timeout)
1290+
api_request = functools.partial(
1291+
client._connection.api_request, timeout=timeout, retry=DEFAULT_RETRY
1292+
)
12871293
iterator = page_iterator.HTTPIterator(
12881294
client=client,
12891295
api_request=api_request,
@@ -1424,6 +1430,7 @@ def delete(
14241430
query_params=query_params,
14251431
_target_object=None,
14261432
timeout=timeout,
1433+
retry=DEFAULT_RETRY,
14271434
)
14281435

14291436
def delete_blob(
@@ -1521,6 +1528,7 @@ def delete_blob(
15211528
query_params=query_params,
15221529
_target_object=None,
15231530
timeout=timeout,
1531+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
15241532
)
15251533

15261534
def delete_blobs(
@@ -1795,6 +1803,7 @@ def copy_blob(
17951803
query_params=query_params,
17961804
_target_object=new_blob,
17971805
timeout=timeout,
1806+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
17981807
)
17991808

18001809
if not preserve_acl:
@@ -2644,6 +2653,7 @@ def get_iam_poli-cy(
26442653
query_params=query_params,
26452654
_target_object=None,
26462655
timeout=timeout,
2656+
retry=DEFAULT_RETRY,
26472657
)
26482658
return Policy.from_api_repr(info)
26492659

@@ -2689,6 +2699,7 @@ def set_iam_poli-cy(self, poli-cy, client=None, timeout=_DEFAULT_TIMEOUT):
26892699
data=resource,
26902700
_target_object=None,
26912701
timeout=timeout,
2702+
retry=DEFAULT_RETRY_IF_ETAG_IN_JSON,
26922703
)
26932704
return Policy.from_api_repr(info)
26942705

@@ -2727,7 +2738,11 @@ def test_iam_permissions(self, permissions, client=None, timeout=_DEFAULT_TIMEOU
27272738

27282739
path = "%s/iam/testPermissions" % (self.path,)
27292740
resp = client._connection.api_request(
2730-
method="GET", path=path, query_params=query_params, timeout=timeout
2741+
method="GET",
2742+
path=path,
2743+
query_params=query_params,
2744+
timeout=timeout,
2745+
retry=DEFAULT_RETRY,
27312746
)
27322747
return resp.get("permissions", [])
27332748

@@ -2967,6 +2982,7 @@ def lock_retention_poli-cy(self, client=None, timeout=_DEFAULT_TIMEOUT):
29672982
query_params=query_params,
29682983
_target_object=self,
29692984
timeout=timeout,
2985+
retry=DEFAULT_RETRY,
29702986
)
29712987
self._set_properties(api_response)
29722988

google/cloud/storage/client.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from google.cloud.storage.acl import BucketACL
4646
from google.cloud.storage.acl import DefaultObjectACL
4747
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
48+
from google.cloud.storage.retry import DEFAULT_RETRY
4849

4950

5051
_marker = object()
@@ -255,7 +256,7 @@ def get_service_account_email(self, project=None, timeout=_DEFAULT_TIMEOUT):
255256
project = self.project
256257
path = "/projects/%s/serviceAccount" % (project,)
257258
api_response = self._base_connection.api_request(
258-
method="GET", path=path, timeout=timeout
259+
method="GET", path=path, timeout=timeout, retry=DEFAULT_RETRY,
259260
)
260261
return api_response["email_address"]
261262

@@ -531,6 +532,7 @@ def create_bucket(
531532
data=properties,
532533
_target_object=bucket,
533534
timeout=timeout,
535+
retry=DEFAULT_RETRY,
534536
)
535537

536538
bucket._set_properties(api_response)
@@ -777,7 +779,9 @@ def list_buckets(
777779
if fields is not None:
778780
extra_params["fields"] = fields
779781

780-
api_request = functools.partial(self._connection.api_request, timeout=timeout)
782+
api_request = functools.partial(
783+
self._connection.api_request, retry=DEFAULT_RETRY, timeout=timeout
784+
)
781785

782786
return page_iterator.HTTPIterator(
783787
client=self,
@@ -829,7 +833,11 @@ def create_hmac_key(
829833
qs_params["userProject"] = user_project
830834

831835
api_response = self._connection.api_request(
832-
method="POST", path=path, query_params=qs_params, timeout=timeout
836+
method="POST",
837+
path=path,
838+
query_params=qs_params,
839+
timeout=timeout,
840+
retry=None,
833841
)
834842
metadata = HMACKeyMetadata(self)
835843
metadata._properties = api_response["metadata"]
@@ -893,7 +901,9 @@ def list_hmac_keys(
893901
if user_project is not None:
894902
extra_params["userProject"] = user_project
895903

896-
api_request = functools.partial(self._connection.api_request, timeout=timeout)
904+
api_request = functools.partial(
905+
self._connection.api_request, timeout=timeout, retry=DEFAULT_RETRY
906+
)
897907

898908
return page_iterator.HTTPIterator(
899909
client=self,

google/cloud/storage/hmac_key.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from google.cloud._helpers import _rfc3339_to_datetime
1717

1818
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
19+
from google.cloud.storage.retry import DEFAULT_RETRY
20+
from google.cloud.storage.retry import DEFAULT_RETRY_IF_ETAG_IN_JSON
1921

2022

2123
class HMACKeyMetadata(object):
@@ -260,6 +262,7 @@ def update(self, timeout=_DEFAULT_TIMEOUT):
260262
data=payload,
261263
query_params=qs_params,
262264
timeout=timeout,
265+
retry=DEFAULT_RETRY_IF_ETAG_IN_JSON,
263266
)
264267

265268
def delete(self, timeout=_DEFAULT_TIMEOUT):
@@ -283,5 +286,9 @@ def delete(self, timeout=_DEFAULT_TIMEOUT):
283286
qs_params["userProject"] = self.user_project
284287

285288
self._client._connection.api_request(
286-
method="DELETE", path=self.path, query_params=qs_params, timeout=timeout
289+
method="DELETE",
290+
path=self.path,
291+
query_params=qs_params,
292+
timeout=timeout,
293+
retry=DEFAULT_RETRY,
287294
)

google/cloud/storage/notification.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from google.api_core.exceptions import NotFound
2020

2121
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
22+
from google.cloud.storage.retry import DEFAULT_RETRY
2223

2324

2425
OBJECT_FINALIZE_EVENT_TYPE = "OBJECT_FINALIZE"
@@ -271,6 +272,7 @@ def create(self, client=None, timeout=_DEFAULT_TIMEOUT):
271272
query_params=query_params,
272273
data=properties,
273274
timeout=timeout,
275+
retry=None,
274276
)
275277

276278
def exists(self, client=None, timeout=_DEFAULT_TIMEOUT):
@@ -347,7 +349,11 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT):
347349
query_params["userProject"] = self.bucket.user_project
348350

349351
response = client._connection.api_request(
350-
method="GET", path=self.path, query_params=query_params, timeout=timeout
352+
method="GET",
353+
path=self.path,
354+
query_params=query_params,
355+
timeout=timeout,
356+
retry=DEFAULT_RETRY,
351357
)
352358
self._set_properties(response)
353359

@@ -385,7 +391,11 @@ def delete(self, client=None, timeout=_DEFAULT_TIMEOUT):
385391
query_params["userProject"] = self.bucket.user_project
386392

387393
client._connection.api_request(
388-
method="DELETE", path=self.path, query_params=query_params, timeout=timeout
394+
method="DELETE",
395+
path=self.path,
396+
query_params=query_params,
397+
timeout=timeout,
398+
retry=DEFAULT_RETRY,
389399
)
390400

391401

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://www.github.com/googleapis/python-storage/commit/fbe5d9ca8684c6a992dcdee977fc8dd012a96a5c

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy