Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/base_transaction.py: 61%
72 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:27 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:27 +0000
1# Copyright 2017 Google LLC All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""Helpers for applying Google Cloud Firestore changes in a transaction."""
17from google.api_core import retry as retries
19from google.cloud.firestore_v1 import types
20from typing import Any, Coroutine, NoReturn, Optional, Union
22_CANT_BEGIN: str
23_CANT_COMMIT: str
24_CANT_RETRY_READ_ONLY: str
25_CANT_ROLLBACK: str
26_EXCEED_ATTEMPTS_TEMPLATE: str
27_INITIAL_SLEEP: float
28_MAX_SLEEP: float
29_MISSING_ID_TEMPLATE: str
30_MULTIPLIER: float
31_WRITE_READ_ONLY: str
34MAX_ATTEMPTS = 5
35"""int: Default number of transaction attempts (with retries)."""
36_CANT_BEGIN: str = "The transaction has already begun. Current transaction ID: {!r}."
37_MISSING_ID_TEMPLATE: str = "The transaction has no transaction ID, so it cannot be {}."
38_CANT_ROLLBACK: str = _MISSING_ID_TEMPLATE.format("rolled back")
39_CANT_COMMIT: str = _MISSING_ID_TEMPLATE.format("committed")
40_WRITE_READ_ONLY: str = "Cannot perform write operation in read-only transaction."
41_INITIAL_SLEEP: float = 1.0
42"""float: Initial "max" for sleep interval. To be used in :func:`_sleep`."""
43_MAX_SLEEP: float = 30.0
44"""float: Eventual "max" sleep time. To be used in :func:`_sleep`."""
45_MULTIPLIER: float = 2.0
46"""float: Multiplier for exponential backoff. To be used in :func:`_sleep`."""
47_EXCEED_ATTEMPTS_TEMPLATE: str = "Failed to commit transaction in {:d} attempts."
48_CANT_RETRY_READ_ONLY: str = "Only read-write transactions can be retried."
51class BaseTransaction(object):
52 """Accumulate read-and-write operations to be sent in a transaction.
54 Args:
55 max_attempts (Optional[int]): The maximum number of attempts for
56 the transaction (i.e. allowing retries). Defaults to
57 :attr:`~google.cloud.firestore_v1.transaction.MAX_ATTEMPTS`.
58 read_only (Optional[bool]): Flag indicating if the transaction
59 should be read-only or should allow writes. Defaults to
60 :data:`False`.
61 """
63 def __init__(self, max_attempts=MAX_ATTEMPTS, read_only=False) -> None:
64 self._max_attempts = max_attempts
65 self._read_only = read_only
66 self._id = None
68 def _add_write_pbs(self, write_pbs) -> NoReturn:
69 raise NotImplementedError
71 def _options_protobuf(
72 self, retry_id: Union[bytes, None]
73 ) -> Optional[types.common.TransactionOptions]:
74 """Convert the current object to protobuf.
76 The ``retry_id`` value is used when retrying a transaction that
77 failed (e.g. due to contention). It is intended to be the "first"
78 transaction that failed (i.e. if multiple retries are needed).
80 Args:
81 retry_id (Union[bytes, NoneType]): Transaction ID of a transaction
82 to be retried.
84 Returns:
85 Optional[google.cloud.firestore_v1.types.TransactionOptions]:
86 The protobuf ``TransactionOptions`` if ``read_only==True`` or if
87 there is a transaction ID to be retried, else :data:`None`.
89 Raises:
90 ValueError: If ``retry_id`` is not :data:`None` but the
91 transaction is read-only.
92 """
93 if retry_id is not None:
94 if self._read_only:
95 raise ValueError(_CANT_RETRY_READ_ONLY)
97 return types.TransactionOptions(
98 read_write=types.TransactionOptions.ReadWrite(
99 retry_transaction=retry_id
100 )
101 )
102 elif self._read_only:
103 return types.TransactionOptions(
104 read_only=types.TransactionOptions.ReadOnly()
105 )
106 else:
107 return None
109 @property
110 def in_progress(self):
111 """Determine if this transaction has already begun.
113 Returns:
114 bool: Indicates if the transaction has started.
115 """
116 return self._id is not None
118 @property
119 def id(self):
120 """Get the current transaction ID.
122 Returns:
123 Optional[bytes]: The transaction ID (or :data:`None` if the
124 current transaction is not in progress).
125 """
126 return self._id
128 def _clean_up(self) -> None:
129 """Clean up the instance after :meth:`_rollback`` or :meth:`_commit``.
131 This intended to occur on success or failure of the associated RPCs.
132 """
133 self._write_pbs = []
134 self._id = None
136 def _begin(self, retry_id=None) -> NoReturn:
137 raise NotImplementedError
139 def _rollback(self) -> NoReturn:
140 raise NotImplementedError
142 def _commit(self) -> Union[list, Coroutine[Any, Any, list]]:
143 raise NotImplementedError
145 def get_all(
146 self,
147 references: list,
148 retry: retries.Retry = None,
149 timeout: float = None,
150 ) -> NoReturn:
151 raise NotImplementedError
153 def get(
154 self,
155 ref_or_query,
156 retry: retries.Retry = None,
157 timeout: float = None,
158 ) -> NoReturn:
159 raise NotImplementedError
162class _BaseTransactional(object):
163 """Provide a callable object to use as a transactional decorater.
165 This is surfaced via
166 :func:`~google.cloud.firestore_v1.transaction.transactional`.
168 Args:
169 to_wrap (Callable[[:class:`~google.cloud.firestore_v1.transaction.Transaction`, ...], Any]):
170 A callable that should be run (and retried) in a transaction.
171 """
173 def __init__(self, to_wrap) -> None:
174 self.to_wrap = to_wrap
175 self.current_id = None
176 """Optional[bytes]: The current transaction ID."""
177 self.retry_id = None
178 """Optional[bytes]: The ID of the first attempted transaction."""
180 def _reset(self) -> None:
181 """Unset the transaction IDs."""
182 self.current_id = None
183 self.retry_id = None
185 def _pre_commit(self, transaction, *args, **kwargs) -> NoReturn:
186 raise NotImplementedError
188 def __call__(self, transaction, *args, **kwargs):
189 raise NotImplementedError