Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make retry parameter public and added in other methods #331

Merged
merged 6 commits into from Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 21 additions & 3 deletions google/cloud/storage/_helpers.py
Expand Up @@ -145,6 +145,7 @@ def reload(
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
retry=DEFAULT_RETRY,
):
"""Reload properties from Cloud Storage.

Expand Down Expand Up @@ -187,6 +188,11 @@ def reload(
:type if_metageneration_not_match: long
:param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
blob's current metageneration does not match the given value.

:type retry: google.api_core.retry.Retry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically either google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy are supported here, and in some cases ConditionalRetryPolicy is the default so despite the complexity we should document it here.

:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
Copy link
Contributor

@andrewsg andrewsg Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Optional) How to retry the RPC. A None value will disable retries. A google.api_core.retry.Retry value will enable retries, and the object will define retriable response codes and errors and configure backoff and timeout options.

A google.cloud.storage.retry.ConditionalRetryPolicy value wraps a Retry object and activates it only if certain conditions are met. This class exists to provide safe defaults for RPC calls that are not technically safe to retry normally (due to potential data duplication or other side-effects) but become safe to retry if a condition such as if_metageneration_match is set.

See the retry.py source code and docstrings in this package (google.cloud.storage.retry) for information on retry types and how to configure them.

"""
client = self._require_client(client)
query_params = self._query_params
Expand All @@ -207,7 +213,7 @@ def reload(
headers=self._encryption_headers(),
_target_object=self,
timeout=timeout,
retry=DEFAULT_RETRY,
retry=retry,
)
self._set_properties(api_response)

Expand Down Expand Up @@ -247,6 +253,7 @@ def patch(
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Sends all changed properties in a PATCH request.

Expand Down Expand Up @@ -286,6 +293,11 @@ def patch(
:type if_metageneration_not_match: long
:param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
blob's current metageneration does not match the given value.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
client = self._require_client(client)
query_params = self._query_params
Expand All @@ -309,7 +321,7 @@ def patch(
query_params=query_params,
_target_object=self,
timeout=timeout,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
retry=retry,
)
self._set_properties(api_response)

Expand All @@ -321,6 +333,7 @@ def update(
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Sends all properties in a PUT request.

Expand Down Expand Up @@ -360,6 +373,11 @@ def update(
:type if_metageneration_not_match: long
:param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
blob's current metageneration does not match the given value.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
client = self._require_client(client)

Expand All @@ -380,7 +398,7 @@ def update(
query_params=query_params,
_target_object=self,
timeout=timeout,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
retry=retry,
)
self._set_properties(api_response)

Expand Down
55 changes: 46 additions & 9 deletions google/cloud/storage/acl.py
Expand Up @@ -85,6 +85,7 @@
"""

from google.cloud.storage.constants import _DEFAULT_TIMEOUT
from google.cloud.storage.retry import DEFAULT_RETRY


class _ACLEntity(object):
Expand Down Expand Up @@ -430,7 +431,7 @@ def _require_client(self, client):
client = self.client
return client

def reload(self, client=None, timeout=_DEFAULT_TIMEOUT):
def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here in the ACL file you're not just exposing retries in the function signature but actually adding a new default, right? @frankyn ACL commands aren't in the consolidated retry strategy doc, do we want retries enabled on them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACLs don't have optimistic concurrency control, therefore we would need to update implementation to rely on storage.objects.patch and storage.buckets.patch instead of ACL specific APIs.

Let's remove ACL retries from this PR for now and open a tracking issue.

"""Reload the ACL data from Cloud Storage.

If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -445,6 +446,11 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT):

Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
path = self.reload_path
client = self._require_client(client)
Expand All @@ -456,13 +462,19 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT):
self.entities.clear()

found = client._connection.api_request(
method="GET", path=path, query_params=query_params, timeout=timeout
method="GET",
path=path,
query_params=query_params,
timeout=timeout,
retry=retry,
)
self.loaded = True
for entry in found.get("items", ()):
self.add_entity(self.entity_from_dict(entry))

def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):
def _save(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've investigated and found that ACL save retries don't support the concurrency control we'd need to have full faith in retries here. Please go ahead and remove them. Thanks!

self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY
):
"""Helper for :meth:`save` and :meth:`save_predefined`.

:type acl: :class:`google.cloud.storage.acl.ACL`, or a compatible list.
Expand All @@ -483,6 +495,11 @@ def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):

Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
query_params = {"projection": "full"}
if predefined is not None:
Expand All @@ -501,13 +518,16 @@ def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):
data={self._URL_PATH_ELEM: list(acl)},
query_params=query_params,
timeout=timeout,
retry=retry,
)
self.entities.clear()
for entry in result.get(self._URL_PATH_ELEM, ()):
self.add_entity(self.entity_from_dict(entry))
self.loaded = True

def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):
def save(
self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY
):
"""Save this ACL for the current bucket.

If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -526,6 +546,11 @@ def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):

Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
if acl is None:
acl = self
Expand All @@ -534,9 +559,11 @@ def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):
save_to_backend = True

if save_to_backend:
self._save(acl, None, client, timeout=timeout)
self._save(acl, None, client, timeout=timeout, retry=retry)

def save_predefined(self, predefined, client=None, timeout=_DEFAULT_TIMEOUT):
def save_predefined(
self, predefined, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY
):
"""Save this ACL for the current bucket using a predefined ACL.

If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -558,11 +585,16 @@ def save_predefined(self, predefined, client=None, timeout=_DEFAULT_TIMEOUT):

Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
predefined = self.validate_predefined(predefined)
self._save(None, predefined, client, timeout=timeout)
self._save(None, predefined, client, timeout=timeout, retry=retry)

def clear(self, client=None, timeout=_DEFAULT_TIMEOUT):
def clear(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
"""Remove all ACL entries.

If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -582,8 +614,13 @@ def clear(self, client=None, timeout=_DEFAULT_TIMEOUT):

Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.

:type retry: google.api_core.retry.Retry
:param retry: (Optional) How to retry the RPC. To modify the default retry behavior,
create a new retry object modeled after this one by calling it a ``with_XXX`` method.
See: https://googleapis.dev/python/google-api-core/latest/retry.html for details.
"""
self.save([], client=client, timeout=timeout)
self.save([], client=client, timeout=timeout, retry=retry)


class BucketACL(ACL):
Expand Down