@@ -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
437542end
0 commit comments