Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 6c716b2

Browse files
committed
Renamed and cleaned up predicate
1 parent b266a29 commit 6c716b2

7 files changed

Lines changed: 58 additions & 34 deletions

File tree

google/cloud/bigtable/data/_async/_read_rows.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@
2929
from google.cloud.bigtable.data.exceptions import _ResetRow
3030
from google.cloud.bigtable.data._helpers import _attempt_timeout_generator
3131
from google.cloud.bigtable.data._helpers import _retry_exception_factory
32-
from google.cloud.bigtable.data._helpers import _read_rows_predicate_with_exceptions
32+
from google.cloud.bigtable.data._helpers import _rst_stream_aware_predicate
3333

34-
from google.api_core import retry as retries
35-
from google.api_core import exceptions as core_exceptions
3634
from google.api_core.retry import exponential_sleep_generator
3735

3836
from google.cloud.bigtable.data._cross_sync import CrossSync
@@ -100,7 +98,7 @@ def __init__(
10098
else:
10199
self.request = query._to_pb(target)
102100
self.target = target
103-
self._predicate = _read_rows_predicate_with_exceptions(*retryable_exceptions)
101+
self._predicate = _rst_stream_aware_predicate(*retryable_exceptions)
104102
self._last_yielded_row_key: bytes | None = None
105103
self._remaining_count: int | None = self.request.rows_limit or None
106104

google/cloud/bigtable/data/_helpers.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -132,31 +132,31 @@ def _retry_exception_factory(
132132
return source_exc, cause_exc
133133

134134

135-
def _read_rows_predicate_with_exceptions(*exception_types: type[Exception]) -> Callable[[Exception], bool]:
136-
"""A custom retry predicate for ReadRows.
137-
135+
def _rst_stream_aware_predicate(
136+
*exception_types: type[Exception],
137+
) -> Callable[[Exception], bool]:
138+
"""A custom retry predicate.
139+
138140
This predicate treats Internal error messages with RST_STREAM errors as
139141
ServiceUnavailable errors and will retry them if the Unavailable exception is retryable.
140142
141143
Args:
142144
exception_types: Exception types to be retried during operation
143-
145+
144146
Returns:
145147
Callable[[Exception], bool]: A retry predicate that takes in an exception and
146148
returns whether or not that exception is retryable
147149
"""
148-
is_exception_type = retries.if_exception_type(*exception_types)
149-
150-
def predicate(exception: Exception) -> bool:
151-
return (isinstance(exception, core_exceptions.InternalServerError) and any(m in exception.message.lower() for m in _RETRYABLE_INTERNAL_ERROR_MESSAGES)) or is_exception_type(exception)
152-
153-
# Treating RST_STREAM internal errors as unavailable errors is only done if ServiceUnavailable is one of the
154-
# given exception types. If InternalServerError is also a retryable exception, we don't necessarily need the
155-
# custom predicate either.
156-
if core_exceptions.ServiceUnavailable in exception_types and core_exceptions.InternalServerError not in exception_types:
157-
return predicate
158-
159-
return is_exception_type
150+
# predicate to check for retryable error types
151+
if_exception_type = retries.if_exception_type(*exception_types)
152+
# special case: treat InternalServerError with rst_stream error message as ServiceUnavailable
153+
rst_check = (
154+
lambda e: core_exceptions.ServiceUnavailable in exception_types
155+
and isinstance(e, core_exceptions.InternalServerError)
156+
and any(m in e.message.lower() for m in _RETRYABLE_INTERNAL_ERROR_MESSAGES)
157+
)
158+
159+
return lambda e: if_exception_type(e) or rst_check(e)
160160

161161

162162
def _get_timeouts(

google/cloud/bigtable/data/_sync_autogen/_read_rows.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from google.cloud.bigtable.data.exceptions import _ResetRow
3030
from google.cloud.bigtable.data._helpers import _attempt_timeout_generator
3131
from google.cloud.bigtable.data._helpers import _retry_exception_factory
32-
from google.cloud.bigtable.data._helpers import _read_rows_predicate_with_exceptions
32+
from google.cloud.bigtable.data._helpers import _rst_stream_aware_predicate
3333
from google.api_core.retry import exponential_sleep_generator
3434
from google.cloud.bigtable.data._cross_sync import CrossSync
3535

@@ -88,7 +88,7 @@ def __init__(
8888
else:
8989
self.request = query._to_pb(target)
9090
self.target = target
91-
self._predicate = _read_rows_predicate_with_exceptions(*retryable_exceptions)
91+
self._predicate = _rst_stream_aware_predicate(*retryable_exceptions)
9292
self._last_yielded_row_key: bytes | None = None
9393
self._remaining_count: int | None = self.request.rows_limit or None
9494

google/cloud/bigtable/data/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def __repr__(self):
142142

143143

144144
# TODO: When working on mutations batcher, rework exception handling to guarantee that
145-
# MutationsExceptionGroup only stores FailedMutationEntryErrors.
145+
# MutationsExceptionGroup only stores FailedMutationEntryErrors.
146146
class MutationsExceptionGroup(_BigtableExceptionGroup):
147147
"""
148148
Represents one or more exceptions that occur during a bulk mutation operation

tests/unit/data/_async/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ async def test_customizable_retryable_errors(
14541454
# Read Rows has its own custom predicate builder that also takes in
14551455
# a list of exceptions
14561456
if is_read_rows_fn:
1457-
predicate_builder = f"google.cloud.bigtable.data.{subpackage}._read_rows._read_rows_predicate_with_exceptions"
1457+
predicate_builder = f"google.cloud.bigtable.data.{subpackage}._read_rows._rst_stream_aware_predicate"
14581458
else:
14591459
predicate_builder = "google.api_core.retry.if_exception_type"
14601460

tests/unit/data/_sync_autogen/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ def test_customizable_retryable_errors(
11601160
retry_fn = f"CrossSync._Sync_Impl.{retry_fn}"
11611161
subpackage = "_sync_autogen"
11621162
if is_read_rows_fn:
1163-
predicate_builder = f"google.cloud.bigtable.data.{subpackage}._read_rows._read_rows_predicate_with_exceptions"
1163+
predicate_builder = f"google.cloud.bigtable.data.{subpackage}._read_rows._rst_stream_aware_predicate"
11641164
else:
11651165
predicate_builder = "google.api_core.retry.if_exception_type"
11661166
with mock.patch(

tests/unit/data/test__helpers.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,19 +222,45 @@ def test_get_timeouts_invalid(self, input_times):
222222
_helpers._align_timeouts(input_times[0], input_times[1])
223223

224224

225-
class TestReadRowsPredicateWithException:
225+
class TestRstStreamAwarePredicate:
226226
@pytest.mark.parametrize(
227227
"retryable_exceptions,exception,expected_is_retryable",
228228
[
229-
([core_exceptions.Aborted, core_exceptions.InternalServerError], core_exceptions.InternalServerError("Sorry"), True),
230-
([core_exceptions.Aborted, core_exceptions.InternalServerError], core_exceptions.DataLoss("Sorry"), False),
231-
([core_exceptions.ServiceUnavailable, core_exceptions.Aborted], core_exceptions.InternalServerError("Sorry"), False),
232-
([core_exceptions.ServiceUnavailable, core_exceptions.Aborted], core_exceptions.InternalServerError(_helpers._RETRYABLE_INTERNAL_ERROR_MESSAGES[0]), True),
233-
([core_exceptions.InternalServerError, core_exceptions.Aborted], core_exceptions.InternalServerError(_helpers._RETRYABLE_INTERNAL_ERROR_MESSAGES[0]), True),
234-
]
229+
(
230+
[core_exceptions.Aborted, core_exceptions.InternalServerError],
231+
core_exceptions.InternalServerError("Sorry"),
232+
True,
233+
),
234+
(
235+
[core_exceptions.Aborted, core_exceptions.InternalServerError],
236+
core_exceptions.DataLoss("Sorry"),
237+
False,
238+
),
239+
(
240+
[core_exceptions.ServiceUnavailable, core_exceptions.Aborted],
241+
core_exceptions.InternalServerError("Sorry"),
242+
False,
243+
),
244+
(
245+
[core_exceptions.ServiceUnavailable, core_exceptions.Aborted],
246+
core_exceptions.InternalServerError(
247+
_helpers._RETRYABLE_INTERNAL_ERROR_MESSAGES[0]
248+
),
249+
True,
250+
),
251+
(
252+
[core_exceptions.InternalServerError, core_exceptions.Aborted],
253+
core_exceptions.InternalServerError(
254+
_helpers._RETRYABLE_INTERNAL_ERROR_MESSAGES[0]
255+
),
256+
True,
257+
),
258+
],
235259
)
236-
def test_ctor_retryable_exceptions_predicate(self, retryable_exceptions, exception, expected_is_retryable):
237-
predicate = _helpers._read_rows_predicate_with_exceptions(*retryable_exceptions)
260+
def test_rst_stream_aware_predicate(
261+
self, retryable_exceptions, exception, expected_is_retryable
262+
):
263+
predicate = _helpers._rst_stream_aware_predicate(*retryable_exceptions)
238264
assert predicate(exception) is expected_is_retryable
239265

240266

0 commit comments

Comments
 (0)