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

Commit b52c871

Browse files
committed
added read_row and read_rows_sharded tests
1 parent 0a9af2d commit b52c871

2 files changed

Lines changed: 214 additions & 8 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1391,7 +1391,7 @@ async def execute_rpc():
13911391
predicate,
13921392
operation_metric.backoff_generator,
13931393
operation_timeout,
1394-
exception_factory=_retry_exception_factory,
1394+
exception_factory=partial(_retry_exception_factory, operation=operation_metric)
13951395
)
13961396

13971397
@CrossSync.convert(replace_symbols={"MutationsBatcherAsync": "MutationsBatcher"})

tests/system/data/test_metrics_async.py

Lines changed: 213 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,38 @@ async def test_read_row_failure_grpc(
543543
544544
No headers expected
545545
"""
546-
pass
546+
await temp_rows.add_row(b"row_key_1")
547+
handler.clear()
548+
exc = Aborted("injected")
549+
num_retryable = 2
550+
for i in range(num_retryable):
551+
error_injector.push(exc)
552+
error_injector.push(PermissionDenied("terminal"))
553+
with pytest.raises(PermissionDenied):
554+
await table.read_row(b"row_key_1", retryable_errors=[Aborted])
555+
# validate counts
556+
assert len(handler.completed_operations) == 1
557+
assert len(handler.completed_attempts) == num_retryable + 1
558+
assert len(handler.cancelled_operations) == 0
559+
# validate operation
560+
operation = handler.completed_operations[0]
561+
assert isinstance(operation, CompletedOperationMetric)
562+
assert operation.final_status.name == "PERMISSION_DENIED"
563+
assert operation.op_type.value == "ReadRows"
564+
assert operation.is_streaming is False
565+
assert len(operation.completed_attempts) == num_retryable + 1
566+
assert operation.cluster_id == "unspecified"
567+
assert operation.zone == "global"
568+
# validate attempts
569+
for i in range(num_retryable):
570+
attempt = handler.completed_attempts[i]
571+
assert isinstance(attempt, CompletedAttemptMetric)
572+
assert attempt.end_status.name == "ABORTED"
573+
assert attempt.gfe_latency_ns is None
574+
final_attempt = handler.completed_attempts[num_retryable]
575+
assert isinstance(final_attempt, CompletedAttemptMetric)
576+
assert final_attempt.end_status.name == "PERMISSION_DENIED"
577+
assert final_attempt.gfe_latency_ns is None
547578

548579
@CrossSync.pytest
549580
async def test_read_row_failure_timeout(self, table, temp_rows, handler):
@@ -552,7 +583,28 @@ async def test_read_row_failure_timeout(self, table, temp_rows, handler):
552583
553584
No grpc headers expected
554585
"""
555-
pass
586+
await temp_rows.add_row(b"row_key_1")
587+
handler.clear()
588+
with pytest.raises(GoogleAPICallError):
589+
await table.read_row(b"row_key_1", operation_timeout=0.001)
590+
# validate counts
591+
assert len(handler.completed_operations) == 1
592+
assert len(handler.completed_attempts) == 1
593+
assert len(handler.cancelled_operations) == 0
594+
# validate operation
595+
operation = handler.completed_operations[0]
596+
assert isinstance(operation, CompletedOperationMetric)
597+
assert operation.final_status.name == "DEADLINE_EXCEEDED"
598+
assert operation.op_type.value == "ReadRows"
599+
assert operation.is_streaming is False
600+
assert len(operation.completed_attempts) == 1
601+
assert operation.cluster_id == "unspecified"
602+
assert operation.zone == "global"
603+
# validate attempt
604+
attempt = handler.completed_attempts[0]
605+
assert isinstance(attempt, CompletedAttemptMetric)
606+
assert attempt.end_status.name == "DEADLINE_EXCEEDED"
607+
assert attempt.gfe_latency_ns is None
556608

557609
@CrossSync.pytest
558610
async def test_read_row_failure_unauthorized(
@@ -561,7 +613,29 @@ async def test_read_row_failure_unauthorized(
561613
"""
562614
Test failure in backend by accessing an unauthorized family
563615
"""
564-
pass
616+
from google.cloud.bigtable.data.row_filters import FamilyNameRegexFilter
617+
618+
with pytest.raises(GoogleAPICallError) as e:
619+
await authorized_view.read_row(b"any_row", row_filter=FamilyNameRegexFilter("unauthorized"))
620+
assert e.value.grpc_status_code.name == "PERMISSION_DENIED"
621+
# validate counts
622+
assert len(handler.completed_operations) == 1
623+
assert len(handler.completed_attempts) == 1
624+
assert len(handler.cancelled_operations) == 0
625+
# validate operation
626+
operation = handler.completed_operations[0]
627+
assert isinstance(operation, CompletedOperationMetric)
628+
assert operation.final_status.name == "PERMISSION_DENIED"
629+
assert operation.op_type.value == "ReadRows"
630+
assert operation.is_streaming is False
631+
assert len(operation.completed_attempts) == 1
632+
assert operation.cluster_id == next(iter(cluster_config.keys()))
633+
assert operation.zone == cluster_config[operation.cluster_id].location.split("/")[-1]
634+
# validate attempt
635+
attempt = handler.completed_attempts[0]
636+
assert isinstance(attempt, CompletedAttemptMetric)
637+
assert attempt.end_status.name == "PERMISSION_DENIED"
638+
assert attempt.gfe_latency_ns >= 0 and attempt.gfe_latency_ns < operation.duration_ns
565639

566640
@CrossSync.pytest
567641
async def test_read_rows_sharded(self, table, temp_rows, handler, cluster_config):
@@ -611,7 +685,46 @@ async def test_read_rows_sharded_failure_grpc(
611685
612686
No headers expected
613687
"""
614-
pass
688+
from google.cloud.bigtable.data.read_rows_query import ReadRowsQuery
689+
from google.cloud.bigtable.data.exceptions import ShardedReadRowsExceptionGroup
690+
691+
await temp_rows.add_row(b"a")
692+
await temp_rows.add_row(b"b")
693+
query1 = ReadRowsQuery(row_keys=[b"a"])
694+
query2 = ReadRowsQuery(row_keys=[b"b"])
695+
handler.clear()
696+
697+
error_injector.push(PermissionDenied("terminal"))
698+
with pytest.raises(ShardedReadRowsExceptionGroup) as e:
699+
await table.read_rows_sharded([query1, query2])
700+
assert len(e.value.exceptions) == 1
701+
assert isinstance(e.value.exceptions[0].__cause__, PermissionDenied)
702+
703+
assert len(handler.completed_operations) == 2
704+
assert len(handler.completed_attempts) == 2
705+
assert len(handler.cancelled_operations) == 0
706+
# sort operations by status
707+
failed_op = next(op for op in handler.completed_operations if op.final_status.name != "OK")
708+
success_op = next(op for op in handler.completed_operations if op.final_status.name == "OK")
709+
# validate failed operation
710+
assert failed_op.final_status.name == "PERMISSION_DENIED"
711+
assert failed_op.op_type.value == "ReadRows"
712+
assert failed_op.is_streaming is True
713+
assert len(failed_op.completed_attempts) == 1
714+
assert failed_op.cluster_id == "unspecified"
715+
assert failed_op.zone == "global"
716+
# validate failed attempt
717+
failed_attempt = failed_op.completed_attempts[0]
718+
assert failed_attempt.end_status.name == "PERMISSION_DENIED"
719+
assert failed_attempt.gfe_latency_ns is None
720+
# validate successful operation
721+
assert success_op.final_status.name == "OK"
722+
assert success_op.op_type.value == "ReadRows"
723+
assert success_op.is_streaming is True
724+
assert len(success_op.completed_attempts) == 1
725+
# validate successful attempt
726+
success_attempt = success_op.completed_attempts[0]
727+
assert success_attempt.end_status.name == "OK"
615728

616729
@CrossSync.pytest
617730
async def test_read_rows_sharded_failure_timeout(
@@ -622,7 +735,38 @@ async def test_read_rows_sharded_failure_timeout(
622735
623736
No grpc headers expected
624737
"""
625-
pass
738+
from google.cloud.bigtable.data.read_rows_query import ReadRowsQuery
739+
from google.cloud.bigtable.data.exceptions import ShardedReadRowsExceptionGroup
740+
from google.api_core.exceptions import DeadlineExceeded
741+
742+
await temp_rows.add_row(b"a")
743+
await temp_rows.add_row(b"b")
744+
query1 = ReadRowsQuery(row_keys=[b"a"])
745+
query2 = ReadRowsQuery(row_keys=[b"b"])
746+
handler.clear()
747+
with pytest.raises(ShardedReadRowsExceptionGroup) as e:
748+
await table.read_rows_sharded([query1, query2], operation_timeout=0.005)
749+
assert len(e.value.exceptions) == 2
750+
for sub_exc in e.value.exceptions:
751+
assert isinstance(sub_exc.__cause__, DeadlineExceeded)
752+
# both shards should fail
753+
assert len(handler.completed_operations) == 2
754+
assert len(handler.completed_attempts) == 2
755+
assert len(handler.cancelled_operations) == 0
756+
# validate operations
757+
for operation in handler.completed_operations:
758+
assert isinstance(operation, CompletedOperationMetric)
759+
assert operation.final_status.name == "DEADLINE_EXCEEDED"
760+
assert operation.op_type.value == "ReadRows"
761+
assert operation.is_streaming is True
762+
assert len(operation.completed_attempts) == 1
763+
assert operation.cluster_id == "unspecified"
764+
assert operation.zone == "global"
765+
# validate attempt
766+
attempt = operation.completed_attempts[0]
767+
assert isinstance(attempt, CompletedAttemptMetric)
768+
assert attempt.end_status.name == "DEADLINE_EXCEEDED"
769+
assert attempt.gfe_latency_ns is None
626770

627771
@CrossSync.pytest
628772
async def test_read_rows_sharded_failure_unauthorized(
@@ -631,7 +775,44 @@ async def test_read_rows_sharded_failure_unauthorized(
631775
"""
632776
Test failure in backend by accessing an unauthorized family
633777
"""
634-
pass
778+
from google.cloud.bigtable.data.read_rows_query import ReadRowsQuery
779+
from google.cloud.bigtable.data.row_filters import FamilyNameRegexFilter
780+
from google.cloud.bigtable.data.exceptions import ShardedReadRowsExceptionGroup
781+
782+
query1 = ReadRowsQuery(row_filter=FamilyNameRegexFilter("unauthorized"))
783+
query2 = ReadRowsQuery(row_filter=FamilyNameRegexFilter(TEST_FAMILY))
784+
handler.clear()
785+
with pytest.raises(ShardedReadRowsExceptionGroup) as e:
786+
await authorized_view.read_rows_sharded([query1, query2])
787+
assert len(e.value.exceptions) == 1
788+
assert isinstance(e.value.exceptions[0].__cause__, GoogleAPICallError)
789+
assert e.value.exceptions[0].__cause__.grpc_status_code.name == "PERMISSION_DENIED"
790+
# one shard will fail, the other will succeed
791+
assert len(handler.completed_operations) == 2
792+
assert len(handler.completed_attempts) == 2
793+
assert len(handler.cancelled_operations) == 0
794+
# sort operations by status
795+
failed_op = next(op for op in handler.completed_operations if op.final_status.name != "OK")
796+
success_op = next(op for op in handler.completed_operations if op.final_status.name == "OK")
797+
# validate failed operation
798+
assert failed_op.final_status.name == "PERMISSION_DENIED"
799+
assert failed_op.op_type.value == "ReadRows"
800+
assert failed_op.is_streaming is True
801+
assert len(failed_op.completed_attempts) == 1
802+
assert failed_op.cluster_id == next(iter(cluster_config.keys()))
803+
assert failed_op.zone == cluster_config[failed_op.cluster_id].location.split("/")[-1]
804+
# validate failed attempt
805+
failed_attempt = failed_op.completed_attempts[0]
806+
assert failed_attempt.end_status.name == "PERMISSION_DENIED"
807+
assert failed_attempt.gfe_latency_ns >= 0 and failed_attempt.gfe_latency_ns < failed_op.duration_ns
808+
# validate successful operation
809+
assert success_op.final_status.name == "OK"
810+
assert success_op.op_type.value == "ReadRows"
811+
assert success_op.is_streaming is True
812+
assert len(success_op.completed_attempts) == 1
813+
# validate successful attempt
814+
success_attempt = success_op.completed_attempts[0]
815+
assert success_attempt.end_status.name == "OK"
635816

636817
@CrossSync.pytest
637818
async def test_read_rows_sharded_failure_mid_stream(
@@ -640,7 +821,32 @@ async def test_read_rows_sharded_failure_mid_stream(
640821
"""
641822
Test failure in grpc stream
642823
"""
643-
pass
824+
from google.cloud.bigtable.data.read_rows_query import ReadRowsQuery
825+
from google.cloud.bigtable.data.exceptions import ShardedReadRowsExceptionGroup
826+
827+
await temp_rows.add_row(b"a")
828+
await temp_rows.add_row(b"b")
829+
query1 = ReadRowsQuery(row_keys=[b"a"])
830+
query2 = ReadRowsQuery(row_keys=[b"b"])
831+
handler.clear()
832+
error_injector.fail_mid_stream = True
833+
error_injector.push(PermissionDenied("terminal"))
834+
error_injector.push(PermissionDenied("terminal"))
835+
with pytest.raises(ShardedReadRowsExceptionGroup) as e:
836+
await table.read_rows_sharded([query1, query2])
837+
assert len(e.value.exceptions) == 2
838+
assert isinstance(e.value.exceptions[0].__cause__, PermissionDenied)
839+
assert len(handler.completed_operations) == 2
840+
assert len(handler.completed_attempts) == 2
841+
assert len(handler.cancelled_operations) == 0
842+
for operation in handler.completed_operations:
843+
assert operation.final_status.name == "PERMISSION_DENIED"
844+
assert operation.op_type.value == "ReadRows"
845+
assert operation.is_streaming is True
846+
assert len(operation.completed_attempts) == 1
847+
# validate attempt
848+
attempt = operation.completed_attempts[0]
849+
assert attempt.end_status.name == "PERMISSION_DENIED"
644850

645851
@CrossSync.pytest
646852
async def test_bulk_mutate_rows(self, table, temp_rows, handler, cluster_config):

0 commit comments

Comments
 (0)