Skip to content

Commit b79ce5f

Browse files
authored
chore(bigtable): Address conformance test failures (#30754)
NoRetry_OutOfOrderError Retry_LastScannerRow
1 parent b5faeb2 commit b79ce5f

File tree

5 files changed

+162
-10
lines changed

5 files changed

+162
-10
lines changed

google-cloud-bigtable/conformance/known_failures.txt

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,41 @@
11
TestCheckAndMutateRow_Generic_CloseClient
22
TestCheckAndMutateRow_Generic_DeadlineExceeded
3+
TestExecuteQuery_BatchesTest
4+
TestExecuteQuery_ChecksumMismatch
5+
TestExecuteQuery_ChunkingTest
6+
TestExecuteQuery_CloseClient
7+
TestExecuteQuery_ConcurrentRequests
38
TestExecuteQuery_EmptyResponse
9+
TestExecuteQuery_ExecuteQueryRespectsDeadline
10+
TestExecuteQuery_FailsOnEmptyMetadata
11+
TestExecuteQuery_FailsOnExecuteQueryMetadata
12+
TestExecuteQuery_FailsOnInvalidType
13+
TestExecuteQuery_FailsOnNotEnoughData
14+
TestExecuteQuery_FailsOnNotEnoughDataWithCompleteRows
15+
TestExecuteQuery_FailsOnStructMissingField
16+
TestExecuteQuery_FailsOnSuccesfulStreamWithNoToken
17+
TestExecuteQuery_FailsOnTypeMismatch
18+
TestExecuteQuery_FailsOnTypeMismatchWithinArray
19+
TestExecuteQuery_FailsOnTypeMismatchWithinMap
20+
TestExecuteQuery_FailsOnTypeMismatchWithinStruct
21+
TestExecuteQuery_HeadersAreSet
22+
TestExecuteQuery_MapAllowsDuplicateKey
23+
TestExecuteQuery_NestedNullsTest
24+
TestExecuteQuery_NullsTest
25+
TestExecuteQuery_PlanRefresh
26+
TestExecuteQuery_PrepareQueryRespectsDeadline
27+
TestExecuteQuery_QueryParams
28+
TestExecuteQuery_RetryTest_ErrorAfterFinalData
29+
TestExecuteQuery_RetryTest_FirstResponse
30+
TestExecuteQuery_RetryTest_MidStream
31+
TestExecuteQuery_RetryTest_ResetCompleteBatch
32+
TestExecuteQuery_RetryTest_ResetPartialBatch
33+
TestExecuteQuery_RetryTest_TokenWithoutData
34+
TestExecuteQuery_RetryTest_WithPlanRefresh
435
TestExecuteQuery_SingleSimpleRow
36+
TestExecuteQuery_StructWithDuplicateColumnNames
37+
TestExecuteQuery_StructWithNoColumnNames
38+
TestExecuteQuery_TypesTest
539
TestMutateRow_Generic_DeadlineExceeded
640
TestMutateRow_Generic_CloseClient
741
TestMutateRows_Generic_DeadlineExceeded
@@ -15,10 +49,8 @@ TestReadRow_Generic_DeadlineExceeded
1549
TestReadRow_Generic_CloseClient
1650
TestReadRow_Retry_WithRoutingCookie
1751
TestReadRow_Retry_WithRetryInfo
18-
TestReadRows_NoRetry_OutOfOrderError
1952
TestReadRows_NoRetry_OutOfOrderError_Reverse
2053
TestReadRows_ReverseScans_FeatureFlag_Enabled
21-
TestReadRows_Retry_LastScannedRow
2254
TestReadRows_Retry_LastScannedRow_Reverse
2355
TestReadRows_Generic_CloseClient
2456
TestReadRows_Generic_DeadlineExceeded

google-cloud-bigtable/conformance/test_proxy/test_proxy.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ def read_rows req, _call
223223
end
224224
rescue Google::Cloud::Error => e
225225
LOGGER.info "ReadRows failed: Caught #{e}"
226-
Google::Bigtable::Testproxy::RowResult.new(
227-
status: make_status(e.code)
226+
Google::Bigtable::Testproxy::RowsResult.new(
227+
status: make_status(e.code, e.message)
228228
)
229229
end
230230
# rubocop:enable Metrics/AbcSize

google-cloud-bigtable/lib/google/cloud/bigtable/chunk_processor.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,23 @@ def validate_reset_row
9494
# or if family name or column qualifier are empty
9595
#
9696
def validate_new_row
97-
raise_if row.key, "A new row cannot have existing state"
98-
raise_if chunk.row_key.empty?, "A row key must be set"
99-
raise_if chunk.reset_row, "A new row cannot be reset"
100-
raise_if last_key == chunk.row_key, "A commit happened but the same key followed"
101-
raise_if chunk.family_name.nil?, "A family must be set"
102-
raise_if chunk.qualifier.nil?, "A column qualifier must be set"
97+
validations = [
98+
[row.key, "A new row cannot have existing state"],
99+
[chunk.row_key.empty?, "A row key must be set"],
100+
[chunk.reset_row, "A new row cannot be reset"],
101+
[last_key == chunk.row_key, "A commit happened but the same key followed"],
102+
[
103+
last_key && chunk.row_key < last_key,
104+
"Out of order row key, must be strictly increasing. " \
105+
"New key: '#{chunk.row_key}', Previous key: '#{last_key}'"
106+
],
107+
[chunk.family_name.nil?, "A family must be set"],
108+
[chunk.qualifier.nil?, "A column qualifier must be set"]
109+
]
110+
111+
validations.each do |condition, message|
112+
raise_if condition, message
113+
end
103114
end
104115

105116
##

google-cloud-bigtable/lib/google/cloud/bigtable/rows_reader.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ def read rows: nil, filter: nil, rows_limit: nil
9292
yield row
9393
@rows_count += 1
9494
end
95+
96+
if res.last_scanned_row_key && !res.last_scanned_row_key.empty?
97+
@chunk_processor.last_key = res.last_scanned_row_key
98+
end
9599
end
96100

97101
@chunk_processor.validate_last_row_complete

google-cloud-bigtable/test/google/cloud/bigtable/table/read_rows_test.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,109 @@ def mock.read_rows _args
434434
_(rows.length).must_equal 1
435435
_(rows.first).must_equal expected_row
436436
end
437+
438+
it "reports an error if the client detects that the server sent row keys out of order" do
439+
mock = Minitest::Mock.new
440+
bigtable.service.mocked_client = mock
441+
table = bigtable.table(instance_id, table_id)
442+
443+
chunks = [
444+
Google::Cloud::Bigtable::V2::ReadRowsResponse::CellChunk.new(
445+
row_key: "row-2",
446+
family_name: Google::Protobuf::StringValue.new(value: "cf"),
447+
qualifier: Google::Protobuf::BytesValue.new(value: "cq"),
448+
value: "v",
449+
commit_row: true
450+
),
451+
Google::Cloud::Bigtable::V2::ReadRowsResponse::CellChunk.new(
452+
row_key: "row-1", # Out of order
453+
family_name: Google::Protobuf::StringValue.new(value: "cf"),
454+
qualifier: Google::Protobuf::BytesValue.new(value: "cq"),
455+
value: "v",
456+
commit_row: true
457+
)
458+
]
459+
responses = [
460+
Google::Cloud::Bigtable::V2::ReadRowsResponse.new(chunks: [chunks[0]]),
461+
Google::Cloud::Bigtable::V2::ReadRowsResponse.new(chunks: [chunks[1]])
462+
]
463+
464+
mock.expect :read_rows, responses,
465+
table_name: table_path(instance_id, table_id),
466+
rows: Google::Cloud::Bigtable::V2::RowSet.new(row_ranges: [Google::Cloud::Bigtable::V2::RowRange.new]),
467+
filter: nil,
468+
rows_limit: nil,
469+
app_profile_id: nil
470+
471+
err = assert_raises Google::Cloud::Error do
472+
table.read_rows.to_a
473+
end
474+
_(err.message).must_match /Out of order row key/
475+
end
476+
477+
it "resumes a read from the last scanned row key after a retryable error" do
478+
mock = Minitest::Mock.new
479+
bigtable.service.mocked_client = mock
480+
table = bigtable.table(instance_id, table_id)
481+
482+
first_mock_responses = [
483+
Google::Cloud::Bigtable::V2::ReadRowsResponse.new(last_scanned_row_key: ""),
484+
"error"
485+
]
486+
first_mock_itr = OpenStruct.new(read_response: first_mock_responses)
487+
def first_mock_itr.each
488+
self.read_response.each do |res|
489+
raise GRPC::DeadlineExceeded, "Deadline exceeded" if res == "error"
490+
yield res
491+
end
492+
end
493+
494+
second_mock_responses = [
495+
Google::Cloud::Bigtable::V2::ReadRowsResponse.new(last_scanned_row_key: "row-a"),
496+
"error"
497+
]
498+
second_mock_itr = OpenStruct.new(read_response: second_mock_responses)
499+
def second_mock_itr.each
500+
self.read_response.each do |res|
501+
raise GRPC::DeadlineExceeded, "Deadline exceeded" if res == "error"
502+
yield res
503+
end
504+
end
505+
506+
final_chunk = Google::Cloud::Bigtable::V2::ReadRowsResponse::CellChunk.new(
507+
row_key: "row-b",
508+
family_name: Google::Protobuf::StringValue.new(value: "cf"),
509+
qualifier: Google::Protobuf::BytesValue.new(value: "cq"),
510+
value: "v",
511+
commit_row: true
512+
)
513+
final_response = [Google::Cloud::Bigtable::V2::ReadRowsResponse.new(chunks: [final_chunk])]
514+
515+
mock.expect :read_rows, first_mock_itr,
516+
table_name: table_path(instance_id, table_id),
517+
rows: Google::Cloud::Bigtable::V2::RowSet.new(row_ranges: [Google::Cloud::Bigtable::V2::RowRange.new]),
518+
filter: nil,
519+
rows_limit: nil,
520+
app_profile_id: nil
521+
522+
mock.expect :read_rows, second_mock_itr,
523+
table_name: table_path(instance_id, table_id),
524+
rows: Google::Cloud::Bigtable::V2::RowSet.new(row_ranges: [Google::Cloud::Bigtable::V2::RowRange.new]),
525+
filter: nil,
526+
rows_limit: nil,
527+
app_profile_id: nil
528+
529+
mock.expect :read_rows, final_response,
530+
table_name: table_path(instance_id, table_id),
531+
rows: Google::Cloud::Bigtable::V2::RowSet.new(
532+
row_ranges: [Google::Cloud::Bigtable::V2::RowRange.new(start_key_open: "row-a")]
533+
),
534+
filter: nil,
535+
rows_limit: nil,
536+
app_profile_id: nil
537+
538+
rows = table.read_rows.to_a
539+
_(rows.map(&:key)).must_equal ["row-b"]
540+
mock.verify
541+
end
437542
end

0 commit comments

Comments
 (0)