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

Commit 7eb50d8

Browse files
committed
added trackers to data model
1 parent 64ce0a4 commit 7eb50d8

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

google/cloud/bigtable/data/_metrics/data_model.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@
2828
from grpc import RpcError
2929
from grpc.aio import AioRpcError
3030

31+
from google.api_core.exceptions import GoogleAPICallError
3132
import google.cloud.bigtable.data.exceptions as bt_exceptions
3233
from google.cloud.bigtable_v2.types.response_params import ResponseParams
3334
from google.cloud.bigtable.data._helpers import TrackedBackoffGenerator
35+
from google.cloud.bigtable.data.exceptions import _MutateRowsIncomplete
36+
from google.cloud.bigtable.data.exceptions import RetryExceptionGroup
3437
from google.protobuf.message import DecodeError
3538

3639
if TYPE_CHECKING:
3740
from google.cloud.bigtable.data._metrics.handlers._base import MetricsHandler
41+
from google.api_core.retry import RetryFailureReason
3842

3943

4044
LOGGER = logging.getLogger(__name__)
@@ -395,6 +399,66 @@ def _exc_to_status(exc: BaseException) -> StatusCode:
395399
return exc.code()
396400
return StatusCode.UNKNOWN
397401

402+
def track_retryable_error(self) -> callable[[Exception], None]:
403+
"""
404+
Used as input to api_core.Retry classes, to track when retryable errors are encountered
405+
406+
Should be passed as on_error callback
407+
"""
408+
409+
def wrapper(exc: Exception) -> None:
410+
try:
411+
# record metadata from failed rpc
412+
if (
413+
isinstance(exc, GoogleAPICallError)
414+
and exc.errors
415+
):
416+
rpc_error = exc.errors[-1]
417+
metadata = list(rpc_error.trailing_metadata()) + list(
418+
rpc_error.initial_metadata()
419+
)
420+
self.add_response_metadata({k: v for k, v in metadata})
421+
except Exception:
422+
# ignore errors in metadata collection
423+
pass
424+
if isinstance(exc, _MutateRowsIncomplete):
425+
# _MutateRowsIncomplete represents a successful rpc with some failed mutations
426+
# mark the attempt as successful
427+
self.end_attempt_with_status(StatusCode.OK)
428+
else:
429+
self.end_attempt_with_status(exc)
430+
return wrapper
431+
432+
def track_terminal_error(self, exception_factory:callable[
433+
[list[Exception], RetryFailureReason, float | None],tuple[Exception, Exception | None],
434+
]) -> callable[[list[Exception], RetryFailureReason, float | None], None]:
435+
"""
436+
Used as input to api_core.Retry classes, to track when terminal errors are encountered
437+
438+
Should be used as a wrapper over an exception_factory callback
439+
"""
440+
def wrapper(
441+
exc_list: list[Exception], reason: RetryFailureReason, timeout_val: float | None
442+
) -> tuple[Exception, Exception | None]:
443+
source_exc, cause_exc = exception_factory(exc_list, reason, timeout_val)
444+
try:
445+
# record metadata from failed rpc
446+
if (
447+
isinstance(source_exc, GoogleAPICallError)
448+
and source_exc.errors
449+
):
450+
rpc_error = source_exc.errors[-1]
451+
metadata = list(rpc_error.trailing_metadata()) + list(
452+
rpc_error.initial_metadata()
453+
)
454+
self.add_response_metadata({k: v for k, v in metadata})
455+
except Exception:
456+
# ignore errors in metadata collection
457+
pass
458+
self.end_with_status(source_exc)
459+
return source_exc, cause_exc
460+
return wrapper
461+
398462
@staticmethod
399463
def _handle_error(message: str) -> None:
400464
"""

0 commit comments

Comments
 (0)