Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b8fca56

Browse files
committed
[gcloud] Retry on connection/read timeout
Revert this comment after a version of `google-cloud-storage` with googleapis/python-storage#727 gets released and ScanForm is updated to use it.
1 parent 4336884 commit b8fca56

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

storages/backends/gcloud.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
)
1717

1818
try:
19+
from google.api_core import exceptions as api_exceptions
20+
from google.api_core import retry
21+
from google.auth import exceptions as auth_exceptions
1922
from google.cloud.exceptions import NotFound
2023
from google.cloud.storage import Blob, Client
2124
from google.cloud.storage.blob import _quote
2225
from google.cloud.storage.retry import (
23-
DEFAULT_RETRY, DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
26+
_RETRYABLE_TYPES, ConditionalRetryPolicy, is_generation_specified,
27+
_ADDITIONAL_RETRYABLE_STATUS_CODES,
2428
)
29+
import requests.exceptions
2530
except ImportError:
2631
raise ImproperlyConfigured("Could not load Google Cloud Storage bindings.\n"
2732
"See https://github.com/GoogleCloudPlatform/gcloud-python")
@@ -30,14 +35,40 @@
3035
CONTENT_ENCODING = 'content_encoding'
3136
CONTENT_TYPE = 'content_type'
3237

38+
# TODO(m1): revert the commit that introduced this after a version of
39+
# `google-cloud-storage` with https://github.com/googleapis/python-storage/pull/727
40+
# gets released and ScanForm is updated to use it.
41+
CUSTOM_RETRYABLE_TYPES = _RETRYABLE_TYPES + (
42+
requests.exceptions.Timeout,
43+
)
44+
45+
46+
def _should_retry(exc):
47+
"""Predicate for determining when to retry."""
48+
if isinstance(exc, CUSTOM_RETRYABLE_TYPES):
49+
return True
50+
elif isinstance(exc, api_exceptions.GoogleAPICallError):
51+
return exc.code in _ADDITIONAL_RETRYABLE_STATUS_CODES
52+
elif isinstance(exc, auth_exceptions.TransportError):
53+
return _should_retry(exc.args[0])
54+
else:
55+
return False
56+
57+
58+
RETRY = retry.Retry(predicate=_should_retry)
59+
60+
RETRY_IF_GENERATION_SPECIFIED = ConditionalRetryPolicy(
61+
RETRY, is_generation_specified, ["query_params"]
62+
)
63+
3364

3465
class GoogleCloudFile(CompressedFileMixin, File):
3566
def __init__(self, name, mode, storage):
3667
self.name = name
3768
self.mime_type = mimetypes.guess_type(name)[0]
3869
self._mode = mode
3970
self._storage = storage
40-
self.blob = storage.bucket.get_blob(name, timeout=storage.timeout)
71+
self.blob = storage.bucket.get_blob(name, timeout=storage.timeout, retry=RETRY)
4172
if not self.blob and 'w' in mode:
4273
self.blob = Blob(
4374
self.name, storage.bucket,
@@ -58,7 +89,8 @@ def _get_file(self):
5889
)
5990
if 'r' in self._mode:
6091
self._is_dirty = False
61-
self.blob.download_to_file(self._file, timeout=self._storage.timeout)
92+
self.blob.download_to_file(self._file, timeout=self._storage.timeout,
93+
retry=RETRY)
6294
self._file.seek(0)
6395
if self._storage.gzip and self.blob.content_encoding == 'gzip':
6496
self._file = self._decompress_file(mode=self._mode, file=self._file)
@@ -140,9 +172,9 @@ def get_default_settings(self):
140172
@property
141173
def retry_if_generation_specified_or_immutable(self):
142174
if self.all_files_immutable:
143-
return DEFAULT_RETRY
175+
return RETRY
144176

145-
return DEFAULT_RETRY_IF_GENERATION_SPECIFIED
177+
return RETRY_IF_GENERATION_SPECIFIED
146178

147179
@property
148180
def client(self):
@@ -242,13 +274,13 @@ def delete(self, name):
242274
def exists(self, name):
243275
if not name: # root element aka the bucket
244276
try:
245-
self.client.get_bucket(self.bucket, timeout=self.timeout)
277+
self.client.get_bucket(self.bucket, timeout=self.timeout, retry=RETRY)
246278
return True
247279
except NotFound:
248280
return False
249281

250282
name = self._normalize_name(clean_name(name))
251-
return bool(self.bucket.get_blob(name, timeout=self.timeout))
283+
return bool(self.bucket.get_blob(name, timeout=self.timeout, retry=RETRY))
252284

253285
def listdir(self, name):
254286
name = self._normalize_name(clean_name(name))
@@ -258,7 +290,7 @@ def listdir(self, name):
258290
name += '/'
259291

260292
iterator = self.bucket.list_blobs(prefix=name, delimiter='/',
261-
timeout=self.timeout)
293+
timeout=self.timeout, retry=RETRY)
262294
blobs = list(iterator)
263295
prefixes = iterator.prefixes
264296

@@ -276,7 +308,7 @@ def listdir(self, name):
276308

277309
def _get_blob(self, name):
278310
# Wrap google.cloud.storage's blob to raise if the file doesn't exist
279-
blob = self.bucket.get_blob(name, timeout=self.timeout)
311+
blob = self.bucket.get_blob(name, timeout=self.timeout, retry=RETRY)
280312

281313
if blob is None:
282314
raise NotFound('File does not exist: {}'.format(name))

0 commit comments

Comments
 (0)