github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/scanner_test.go (about) 1 // Copyright (C) 2017 The GoHBase Authors. All rights reserved. 2 // This file is part of GoHBase. 3 // Use of this source code is governed by the Apache License 2.0 4 // that can be found in the COPYING file. 5 6 package gohbase 7 8 import ( 9 "context" 10 "errors" 11 "fmt" 12 "io" 13 "log/slog" 14 "reflect" 15 "sync" 16 "testing" 17 18 "github.com/tsuna/gohbase/filter" 19 "github.com/tsuna/gohbase/hrpc" 20 "github.com/tsuna/gohbase/pb" 21 "github.com/tsuna/gohbase/region" 22 "github.com/tsuna/gohbase/test" 23 "github.com/tsuna/gohbase/test/mock" 24 "go.uber.org/mock/gomock" 25 "google.golang.org/protobuf/proto" 26 ) 27 28 func cp(i uint64) *uint64 { 29 j := i 30 return &j 31 } 32 33 type scanMatcher struct { 34 scan *hrpc.Scan 35 } 36 37 func (c *scanMatcher) Matches(x interface{}) bool { 38 s, ok := x.(*hrpc.Scan) 39 if c.scan.Region() == nil { 40 c.scan.SetRegion(region.NewInfo(0, nil, nil, nil, nil, nil)) 41 } 42 if s.Region() == nil { 43 s.SetRegion(region.NewInfo(0, nil, nil, nil, nil, nil)) 44 } 45 return ok && proto.Equal(c.scan.ToProto(), s.ToProto()) 46 } 47 48 func (c *scanMatcher) String() string { 49 return fmt.Sprintf("is equal to %s", c.scan) 50 } 51 52 var resultsPB = []*pb.Result{ 53 // region 1 54 &pb.Result{ 55 Cell: []*pb.Cell{ 56 &pb.Cell{Row: []byte("a"), Family: []byte("A"), Qualifier: []byte("1")}, 57 &pb.Cell{Row: []byte("a"), Family: []byte("A"), Qualifier: []byte("2")}, 58 &pb.Cell{Row: []byte("a"), Family: []byte("B"), Qualifier: []byte("1")}, 59 }, 60 }, 61 &pb.Result{ 62 Cell: []*pb.Cell{ 63 &pb.Cell{Row: []byte("b"), Family: []byte("A"), Qualifier: []byte("1")}, 64 &pb.Cell{Row: []byte("b"), Family: []byte("B"), Qualifier: []byte("2")}, 65 }, 66 }, 67 // region 2 68 &pb.Result{ 69 Cell: []*pb.Cell{ 70 &pb.Cell{Row: []byte("bar"), Family: []byte("C"), Qualifier: []byte("1")}, 71 &pb.Cell{Row: []byte("baz"), Family: []byte("C"), Qualifier: []byte("2")}, 72 &pb.Cell{Row: []byte("baz"), Family: []byte("C"), Qualifier: []byte("2")}, 73 }, 74 }, 75 // region 3 76 &pb.Result{ 77 Cell: []*pb.Cell{ 78 &pb.Cell{Row: []byte("yolo"), Family: []byte("D"), Qualifier: []byte("1")}, 79 &pb.Cell{Row: []byte("yolo"), Family: []byte("D"), Qualifier: []byte("2")}, 80 }, 81 }, 82 } 83 84 var ( 85 table = []byte("test") 86 region1 = region.NewInfo(0, nil, table, []byte("table,,bar,whatever"), nil, []byte("bar")) 87 region2 = region.NewInfo(0, nil, table, 88 []byte("table,bar,foo,whatever"), []byte("bar"), []byte("foo")) 89 region3 = region.NewInfo(0, nil, table, []byte("table,foo,,whatever"), []byte("foo"), nil) 90 ) 91 92 func dup(a []*pb.Result) []*pb.Result { 93 b := make([]*pb.Result, len(a)) 94 copy(b, a) 95 return b 96 } 97 98 func testCallClose(scan *hrpc.Scan, c *mock.MockRPCClient, scannerID uint64, 99 group *sync.WaitGroup, t *testing.T) { 100 // t.Helper() 101 102 s, err := hrpc.NewScanRange(context.Background(), table, nil, nil, 103 hrpc.ScannerID(scannerID), hrpc.CloseScanner(), hrpc.NumberOfRows(0)) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(arg0 interface{}) { 109 group.Done() 110 }).Return(&pb.ScanResponse{}, nil).Times(1) 111 } 112 113 func TestScanner(t *testing.T) { 114 ctrl := test.NewController(t) 115 defer ctrl.Finish() 116 c := mock.NewMockRPCClient(ctrl) 117 118 scan, err := hrpc.NewScan(context.Background(), table, hrpc.NumberOfRows(2)) 119 if err != nil { 120 t.Fatal(err) 121 } 122 123 var scannerID uint64 = 42 124 scanner := newScanner(c, scan, slog.Default()) 125 126 s, err := hrpc.NewScanRange(scan.Context(), table, nil, nil, 127 hrpc.NumberOfRows(2)) 128 if err != nil { 129 t.Fatal(err) 130 } 131 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 132 rpc.SetRegion(region1) 133 }).Return(&pb.ScanResponse{ 134 ScannerId: cp(scannerID), 135 MoreResultsInRegion: proto.Bool(true), 136 Results: dup(resultsPB[:1]), 137 }, nil).Times(1) 138 139 s, err = hrpc.NewScanRange(scan.Context(), table, nil, nil, 140 hrpc.ScannerID(scannerID), hrpc.NumberOfRows(2)) 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 c.EXPECT().SendRPC(&scanMatcher{ 146 scan: s, 147 }).Do(func(rpc hrpc.Call) { 148 rpc.SetRegion(region1) 149 }).Return(&pb.ScanResponse{ 150 Results: dup(resultsPB[1:2]), 151 }, nil).Times(1) 152 153 scannerID++ 154 155 s, err = hrpc.NewScanRange(scan.Context(), table, 156 []byte("bar"), nil, hrpc.NumberOfRows(2)) 157 if err != nil { 158 t.Fatal(err) 159 } 160 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 161 rpc.SetRegion(region2) 162 }).Return(&pb.ScanResponse{ 163 ScannerId: cp(scannerID), 164 Results: dup(resultsPB[2:3]), 165 }, nil).Times(1) 166 167 scannerID++ 168 169 s, err = hrpc.NewScanRange(scan.Context(), table, []byte("foo"), nil, 170 hrpc.NumberOfRows(2)) 171 if err != nil { 172 t.Fatal(err) 173 } 174 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 175 rpc.SetRegion(region3) 176 }).Return(&pb.ScanResponse{ 177 ScannerId: cp(scannerID), 178 Results: dup(resultsPB[3:4]), 179 MoreResults: proto.Bool(false), 180 }, nil).Times(1) 181 182 var rs []*hrpc.Result 183 for { 184 r, err := scanner.Next() 185 if err == io.EOF { 186 break 187 } 188 if err != nil { 189 t.Fatal(err) 190 } 191 rs = append(rs, r) 192 } 193 194 var expected []*hrpc.Result 195 for _, r := range resultsPB { 196 expected = append(expected, hrpc.ToLocalResult(r)) 197 } 198 199 if !reflect.DeepEqual(expected, rs) { 200 t.Fatalf("expected %v, got %v", expected, rs) 201 } 202 } 203 204 var cells = []*pb.Cell{ 205 &pb.Cell{Row: []byte("a"), Family: []byte("A"), Qualifier: []byte("1")}, // 0 206 &pb.Cell{Row: []byte("a"), Family: []byte("A"), Qualifier: []byte("2")}, 207 &pb.Cell{Row: []byte("a"), Family: []byte("A"), Qualifier: []byte("3")}, 208 &pb.Cell{Row: []byte("b"), Family: []byte("B"), Qualifier: []byte("1")}, // 3 209 &pb.Cell{Row: []byte("b"), Family: []byte("B"), Qualifier: []byte("2")}, // 4 210 &pb.Cell{Row: []byte("bar"), Family: []byte("B"), Qualifier: []byte("1")}, // 5 211 &pb.Cell{Row: []byte("bar"), Family: []byte("B"), Qualifier: []byte("2")}, 212 &pb.Cell{Row: []byte("bar"), Family: []byte("B"), Qualifier: []byte("3")}, // 7 213 &pb.Cell{Row: []byte("foo"), Family: []byte("F"), Qualifier: []byte("1")}, // 8 214 &pb.Cell{Row: []byte("foo"), Family: []byte("F"), Qualifier: []byte("2")}, // 9 215 } 216 217 func TestPartialResults(t *testing.T) { 218 scan, err := hrpc.NewScan(context.Background(), table) 219 if err != nil { 220 t.Fatal(err) 221 } 222 expected := []*hrpc.Result{ 223 hrpc.ToLocalResult(&pb.Result{Cell: cells[:3]}), 224 hrpc.ToLocalResult(&pb.Result{Cell: cells[3:5]}), 225 hrpc.ToLocalResult(&pb.Result{Cell: cells[5:8]}), 226 hrpc.ToLocalResult(&pb.Result{Cell: cells[8:]}), 227 } 228 testPartialResults(t, scan, expected) 229 } 230 231 func TestAllowPartialResults(t *testing.T) { 232 scan, err := hrpc.NewScan(context.Background(), table, hrpc.AllowPartialResults()) 233 if err != nil { 234 t.Fatal(err) 235 } 236 expected := []*hrpc.Result{ 237 hrpc.ToLocalResult(&pb.Result{Cell: cells[:3], Partial: proto.Bool(true)}), 238 hrpc.ToLocalResult(&pb.Result{Cell: cells[3:4], Partial: proto.Bool(true)}), 239 hrpc.ToLocalResult(&pb.Result{Cell: cells[4:5], Partial: proto.Bool(true)}), 240 hrpc.ToLocalResult(&pb.Result{Cell: cells[5:7], Partial: proto.Bool(true)}), 241 hrpc.ToLocalResult(&pb.Result{Cell: cells[7:8], Partial: proto.Bool(true)}), 242 // empty list 243 hrpc.ToLocalResult(&pb.Result{Cell: cells[8:8], Partial: proto.Bool(true)}), 244 hrpc.ToLocalResult(&pb.Result{Cell: cells[8:9], Partial: proto.Bool(true)}), 245 hrpc.ToLocalResult(&pb.Result{Cell: cells[9:], Partial: proto.Bool(true)}), 246 } 247 testPartialResults(t, scan, expected) 248 } 249 250 func TestScanMetrics(t *testing.T) { 251 scanned, filtered := rowsScanned, rowsFiltered 252 i0, i1, i2, i4 := int64(0), int64(1), int64(2), int64(4) 253 tcases := []struct { 254 description string 255 trackScanMetrics func(call hrpc.Call) error 256 filter func(call hrpc.Call) error 257 results []*pb.Result 258 scanMetrics *pb.ScanMetrics 259 expectedResults []*hrpc.Result 260 expectedRowsScanned int64 261 expectedRowsFiltered int64 262 }{ 263 { 264 description: "ScanMetrics not enabled", 265 results: []*pb.Result{ 266 {Cell: cells[:3]}, 267 }, 268 scanMetrics: nil, 269 expectedResults: []*hrpc.Result{hrpc.ToLocalResult(&pb.Result{Cell: cells[:3]})}, 270 }, 271 { 272 description: "Empty results", 273 trackScanMetrics: hrpc.TrackScanMetrics(), 274 results: nil, 275 scanMetrics: nil, 276 expectedResults: nil, 277 expectedRowsScanned: 0, 278 expectedRowsFiltered: 0, 279 }, 280 { 281 description: "ScanMetrics: 1 row scanned", 282 trackScanMetrics: hrpc.TrackScanMetrics(), 283 results: []*pb.Result{ 284 {Cell: cells[:3]}, 285 }, 286 scanMetrics: &pb.ScanMetrics{ 287 Metrics: []*pb.NameInt64Pair{ 288 { 289 Name: &scanned, 290 Value: &i1, 291 }, 292 { 293 Name: &filtered, 294 Value: &i0, 295 }, 296 }, 297 }, 298 expectedResults: []*hrpc.Result{toLocalResult(&pb.Result{Cell: cells[:3]})}, 299 expectedRowsScanned: 1, 300 expectedRowsFiltered: 0, 301 }, 302 { 303 description: "ScanMetrics: 2 rows scanned", 304 trackScanMetrics: hrpc.TrackScanMetrics(), 305 results: []*pb.Result{ 306 {Cell: cells[:5]}, 307 }, 308 scanMetrics: &pb.ScanMetrics{ 309 Metrics: []*pb.NameInt64Pair{ 310 { 311 Name: &scanned, 312 Value: &i2, 313 }, 314 { 315 Name: &filtered, 316 Value: &i0, 317 }, 318 }, 319 }, 320 expectedResults: []*hrpc.Result{toLocalResult(&pb.Result{Cell: cells[:5]})}, 321 expectedRowsScanned: 2, 322 expectedRowsFiltered: 0, 323 }, 324 { 325 description: "ScanMetrics: 4 rows scanned, 2 row filtered", 326 trackScanMetrics: hrpc.TrackScanMetrics(), 327 filter: hrpc.Filters(filter.NewPrefixFilter([]byte("b"))), 328 results: []*pb.Result{ 329 {Cell: cells}, 330 }, 331 scanMetrics: &pb.ScanMetrics{ 332 Metrics: []*pb.NameInt64Pair{ 333 { 334 Name: &scanned, 335 Value: &i4, 336 }, 337 { 338 Name: &filtered, 339 Value: &i2, 340 }, 341 }, 342 }, 343 expectedResults: []*hrpc.Result{toLocalResult(&pb.Result{Cell: cells})}, 344 expectedRowsScanned: 4, 345 expectedRowsFiltered: 2, 346 }, 347 { 348 description: "ScanMetrics: 2 rows scanned, 1 row filtered", 349 trackScanMetrics: hrpc.TrackScanMetrics(), 350 filter: hrpc.Filters(filter.NewPrefixFilter([]byte("a"))), 351 results: []*pb.Result{ 352 {Cell: cells[:5]}, 353 }, 354 scanMetrics: &pb.ScanMetrics{ 355 Metrics: []*pb.NameInt64Pair{ 356 { 357 Name: &scanned, 358 Value: &i2, 359 }, 360 { 361 Name: &filtered, 362 Value: &i1, 363 }, 364 }, 365 }, 366 expectedResults: []*hrpc.Result{toLocalResult(&pb.Result{Cell: cells[:5]})}, 367 expectedRowsScanned: 2, 368 expectedRowsFiltered: 1, 369 }, 370 { 371 description: "ScanMetrics: 0 rows scanned, 1 row filtered", 372 trackScanMetrics: hrpc.TrackScanMetrics(), 373 filter: hrpc.Filters(filter.NewPrefixFilter([]byte("a"))), 374 results: []*pb.Result{ 375 {Cell: cells[:3]}, 376 }, 377 scanMetrics: &pb.ScanMetrics{ 378 Metrics: []*pb.NameInt64Pair{ 379 { 380 Name: &scanned, 381 Value: &i0, 382 }, 383 { 384 Name: &filtered, 385 Value: &i1, 386 }, 387 }, 388 }, 389 expectedResults: []*hrpc.Result{toLocalResult(&pb.Result{Cell: cells[:3]})}, 390 expectedRowsScanned: 0, 391 expectedRowsFiltered: 1, 392 }, 393 } 394 395 ctrl := test.NewController(t) 396 defer ctrl.Finish() 397 c := mock.NewMockRPCClient(ctrl) 398 399 for _, tcase := range tcases { 400 t.Run(tcase.description, func(t *testing.T) { 401 ctx := context.Background() 402 var scan *hrpc.Scan 403 var err error 404 if tcase.trackScanMetrics != nil && tcase.filter != nil { 405 scan, err = hrpc.NewScan(ctx, table, tcase.trackScanMetrics, tcase.filter) 406 } else if tcase.trackScanMetrics != nil { 407 scan, err = hrpc.NewScan(ctx, table, tcase.trackScanMetrics) 408 } else { 409 scan, err = hrpc.NewScan(ctx, table) 410 } 411 412 if err != nil { 413 t.Fatal(err) 414 } 415 416 sc := newScanner(c, scan, slog.Default()) 417 418 c.EXPECT().SendRPC(&scanMatcher{scan: scan}).Return(&pb.ScanResponse{ 419 Results: tcase.results, 420 ScanMetrics: tcase.scanMetrics, 421 }, nil).Times(1) 422 423 var res []*hrpc.Result 424 for { 425 var r *hrpc.Result 426 r, err = sc.Next() 427 if err == io.EOF { 428 break 429 } 430 if err != nil { 431 t.Fatal(err) 432 } 433 res = append(res, r) 434 } 435 436 actualMetrics := sc.GetScanMetrics() 437 438 if tcase.trackScanMetrics == nil && actualMetrics != nil { 439 t.Fatalf("Got non-nil scan metrics when not enabled: %v", actualMetrics) 440 } 441 442 if tcase.expectedRowsScanned != actualMetrics[rowsScanned] { 443 t.Errorf("Did not get expected rows scanned - expected: %d, actual %d", 444 tcase.expectedRowsScanned, actualMetrics[rowsScanned]) 445 } 446 447 if tcase.expectedRowsFiltered != actualMetrics[rowsFiltered] { 448 t.Errorf("Did not get expected rows filtered - expected: %d, actual %d", 449 tcase.expectedRowsFiltered, actualMetrics[rowsFiltered]) 450 } 451 452 if !reflect.DeepEqual(tcase.expectedResults, res) { 453 t.Fatalf("expected: %+v\ngot: %+v", tcase.expectedResults, res) 454 } 455 }) 456 } 457 } 458 459 func TestErrorScanFromID(t *testing.T) { 460 scan, err := hrpc.NewScan(context.Background(), table) 461 if err != nil { 462 t.Fatal(err) 463 } 464 expected := []*hrpc.Result{ 465 hrpc.ToLocalResult(&pb.Result{Cell: cells[:3]}), 466 hrpc.ToLocalResult(&pb.Result{Cell: cells[3:4]}), 467 } 468 testErrorScanFromID(t, scan, expected) 469 } 470 471 func TestErrorScanFromIDAllowPartials(t *testing.T) { 472 scan, err := hrpc.NewScan(context.Background(), table, hrpc.AllowPartialResults()) 473 if err != nil { 474 t.Fatal(err) 475 } 476 expected := []*hrpc.Result{ 477 hrpc.ToLocalResult(&pb.Result{Cell: cells[:3]}), 478 hrpc.ToLocalResult(&pb.Result{Cell: cells[3:4]}), 479 } 480 testErrorScanFromID(t, scan, expected) 481 } 482 483 func TestErrorFirstFetchNoMetrics(t *testing.T) { 484 ctrl := test.NewController(t) 485 defer ctrl.Finish() 486 c := mock.NewMockRPCClient(ctrl) 487 488 scan, err := hrpc.NewScan(context.Background(), table) 489 if err != nil { 490 t.Fatal(err) 491 } 492 scanner := newScanner(c, scan, slog.Default()) 493 494 srange, err := hrpc.NewScanRange(context.Background(), table, nil, nil) 495 if err != nil { 496 t.Fatal(err) 497 } 498 499 outErr := errors.New("WTF") 500 c.EXPECT().SendRPC(&scanMatcher{scan: srange}).Do(func(rpc hrpc.Call) { 501 rpc.SetRegion(region1) 502 }).Return(nil, outErr).Times(1) 503 504 var r *hrpc.Result 505 var rs []*hrpc.Result 506 for { 507 r, err = scanner.Next() 508 if r != nil { 509 rs = append(rs, r) 510 } 511 if err != nil { 512 break 513 } 514 } 515 if err != outErr { 516 t.Errorf("Expected error %v, got error %v", outErr, err) 517 } 518 if len(rs) != 0 { 519 t.Fatalf("expected no results, got %v", rs) 520 } 521 } 522 523 func TestErrorFirstFetchWithMetrics(t *testing.T) { 524 ctrl := test.NewController(t) 525 defer ctrl.Finish() 526 c := mock.NewMockRPCClient(ctrl) 527 528 scan, err := hrpc.NewScan(context.Background(), table, hrpc.TrackScanMetrics()) 529 if err != nil { 530 t.Fatal(err) 531 } 532 scanner := newScanner(c, scan, slog.Default()) 533 534 srange, err := hrpc.NewScanRange(context.Background(), table, nil, nil, 535 hrpc.TrackScanMetrics()) 536 if err != nil { 537 t.Fatal(err) 538 } 539 540 outErr := errors.New("WTF") 541 c.EXPECT().SendRPC(&scanMatcher{scan: srange}).Do(func(rpc hrpc.Call) { 542 rpc.SetRegion(region1) 543 }).Return(nil, outErr).Times(1) 544 545 var r *hrpc.Result 546 var rs []*hrpc.Result 547 for { 548 r, err = scanner.Next() 549 if r != nil { 550 rs = append(rs, r) 551 } 552 if err != nil { 553 break 554 } 555 } 556 if err != outErr { 557 t.Errorf("Expected error %v, got error %v", outErr, err) 558 } 559 if len(rs) != 0 { 560 t.Fatalf("expected no results, got %v", rs) 561 } 562 } 563 564 func testErrorScanFromID(t *testing.T, scan *hrpc.Scan, out []*hrpc.Result) { 565 ctrl := test.NewController(t) 566 defer ctrl.Finish() 567 c := mock.NewMockRPCClient(ctrl) 568 569 var wg sync.WaitGroup 570 wg.Add(1) 571 defer wg.Wait() 572 573 var scannerID uint64 = 42 574 scanner := newScanner(c, scan, slog.Default()) 575 576 srange, err := hrpc.NewScanRange(scan.Context(), table, nil, nil, scan.Options()...) 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 c.EXPECT().SendRPC(&scanMatcher{scan: srange}).Do(func(rpc hrpc.Call) { 582 rpc.SetRegion(region1) 583 }).Return(&pb.ScanResponse{ 584 ScannerId: cp(scannerID), 585 MoreResultsInRegion: proto.Bool(true), 586 Results: []*pb.Result{ 587 &pb.Result{Cell: cells[:3]}, 588 &pb.Result{Cell: cells[3:4]}, 589 }, 590 }, nil).Times(1) 591 592 outErr := errors.New("WTF") 593 594 sid, err := hrpc.NewScanRange(scan.Context(), table, nil, nil, 595 hrpc.ScannerID(scannerID)) 596 if err != nil { 597 t.Fatal(err) 598 } 599 c.EXPECT().SendRPC(&scanMatcher{scan: sid}).Do(func(rpc hrpc.Call) { 600 rpc.SetRegion(region1) 601 }).Return(nil, outErr).Times(1) 602 603 // expect scan close rpc to be sent 604 testCallClose(sid, c, scannerID, &wg, t) 605 606 var r *hrpc.Result 607 var rs []*hrpc.Result 608 for { 609 r, err = scanner.Next() 610 if r != nil { 611 rs = append(rs, r) 612 } 613 if err != nil { 614 break 615 } 616 } 617 618 if err != outErr { 619 t.Errorf("Expected error %v, got error %v", outErr, err) 620 } 621 if !reflect.DeepEqual(out, rs) { 622 t.Fatalf("expected %v, got %v", out, rs) 623 } 624 } 625 626 func testPartialResults(t *testing.T, scan *hrpc.Scan, expected []*hrpc.Result) { 627 t.Helper() 628 ctrl := gomock.NewController(t) 629 defer ctrl.Finish() 630 c := mock.NewMockRPCClient(ctrl) 631 632 tcase := []struct { 633 region hrpc.RegionInfo 634 results []*pb.Result 635 moreResultsInRegion bool 636 scanFromID bool 637 }{ 638 { 639 region: region1, 640 results: []*pb.Result{ 641 &pb.Result{Cell: cells[:3], Partial: proto.Bool(true)}, 642 &pb.Result{Cell: cells[3:4], Partial: proto.Bool(true)}, 643 }, 644 moreResultsInRegion: true, 645 }, 646 { // end of region, should return row b 647 region: region1, 648 results: []*pb.Result{ 649 &pb.Result{Cell: cells[4:5], Partial: proto.Bool(true)}, 650 }, 651 scanFromID: true, 652 }, 653 { // half a row in a result in the same response - unlikely, but why not 654 region: region2, 655 results: []*pb.Result{ 656 &pb.Result{Cell: cells[5:7], Partial: proto.Bool(true)}, 657 &pb.Result{Cell: cells[7:8], Partial: proto.Bool(true)}, 658 }, 659 moreResultsInRegion: true, 660 }, 661 { // empty result, last in region 662 region: region2, 663 results: []*pb.Result{&pb.Result{Cell: cells[8:8], Partial: proto.Bool(true)}}, 664 scanFromID: true, 665 }, 666 { 667 region: region3, 668 results: []*pb.Result{ 669 &pb.Result{Cell: cells[8:9], Partial: proto.Bool(true)}, 670 }, 671 moreResultsInRegion: true, 672 }, 673 { // last row 674 region: region3, 675 results: []*pb.Result{ 676 &pb.Result{Cell: cells[9:], Partial: proto.Bool(true)}, 677 }, 678 scanFromID: true, 679 }, 680 } 681 682 var scannerID uint64 683 scanner := newScanner(c, scan, slog.Default()) 684 ctx := scan.Context() 685 for _, partial := range tcase { 686 partial := partial 687 var s *hrpc.Scan 688 var err error 689 if partial.scanFromID { 690 s, err = hrpc.NewScanRange(ctx, table, partial.region.StartKey(), nil, 691 hrpc.ScannerID(scannerID)) 692 } else { 693 s, err = hrpc.NewScanRange(ctx, table, partial.region.StartKey(), nil, 694 scan.Options()...) 695 scannerID++ 696 } 697 if err != nil { 698 t.Fatal(err) 699 } 700 701 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 702 rpc.SetRegion(partial.region) 703 }).Return(&pb.ScanResponse{ 704 ScannerId: cp(scannerID), 705 MoreResultsInRegion: &partial.moreResultsInRegion, 706 Results: partial.results, 707 }, nil).Times(1) 708 } 709 710 var rs []*hrpc.Result 711 for { 712 r, err := scanner.Next() 713 if err == io.EOF { 714 break 715 } 716 if err != nil { 717 t.Fatal(err) 718 } 719 rs = append(rs, r) 720 } 721 722 if !reflect.DeepEqual(expected, rs) { 723 t.Fatalf("expected %v, got %s", expected, rs) 724 } 725 } 726 727 func TestReversedScanner(t *testing.T) { 728 ctrl := test.NewController(t) 729 defer ctrl.Finish() 730 c := mock.NewMockRPCClient(ctrl) 731 732 ctx := context.Background() 733 scan, err := hrpc.NewScan(ctx, table, hrpc.Reversed()) 734 if err != nil { 735 t.Fatal(err) 736 } 737 738 var scannerID uint64 = 42 739 740 scanner := newScanner(c, scan, slog.Default()) 741 ctx = scan.Context() 742 s, err := hrpc.NewScanRange(ctx, table, nil, nil, hrpc.Reversed()) 743 if err != nil { 744 t.Fatal(err) 745 } 746 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 747 rpc.SetRegion(region3) 748 }).Return(&pb.ScanResponse{ 749 ScannerId: cp(scannerID), 750 Results: dup(resultsPB[3:4]), 751 }, nil).Times(1) 752 753 s, err = hrpc.NewScanRange(ctx, table, 754 append([]byte("fon"), rowPadding...), nil, hrpc.Reversed()) 755 if err != nil { 756 t.Fatal(err) 757 } 758 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 759 rpc.SetRegion(region2) 760 }).Return(&pb.ScanResponse{ 761 ScannerId: cp(scannerID), 762 Results: dup(resultsPB[2:3]), 763 }, nil).Times(1) 764 765 s, err = hrpc.NewScanRange(ctx, table, 766 append([]byte("baq"), rowPadding...), nil, hrpc.Reversed()) 767 if err != nil { 768 t.Fatal(err) 769 } 770 c.EXPECT().SendRPC(&scanMatcher{scan: s}).Do(func(rpc hrpc.Call) { 771 rpc.SetRegion(region1) 772 }).Return(&pb.ScanResponse{ 773 MoreResultsInRegion: proto.Bool(true), 774 ScannerId: cp(scannerID), 775 Results: dup(resultsPB[1:2]), 776 }, nil).Times(1) 777 778 s, err = hrpc.NewScanRange(ctx, table, nil, nil, hrpc.ScannerID(scannerID)) 779 if err != nil { 780 t.Fatal(err) 781 } 782 c.EXPECT().SendRPC(&scanMatcher{ 783 scan: s, 784 }).Do(func(rpc hrpc.Call) { 785 rpc.SetRegion(region1) 786 }).Return(&pb.ScanResponse{ 787 Results: dup(resultsPB[:1]), 788 }, nil).Times(1) 789 790 var rs []*hrpc.Result 791 for { 792 r, err := scanner.Next() 793 if err == io.EOF { 794 break 795 } 796 if err != nil { 797 t.Fatal(err) 798 } 799 rs = append(rs, r) 800 } 801 802 var expected []*hrpc.Result 803 for i := len(resultsPB) - 1; i >= 0; i-- { 804 expected = append(expected, hrpc.ToLocalResult(resultsPB[i])) 805 } 806 807 if !reflect.DeepEqual(expected, rs) { 808 t.Fatalf("expected %v, got %v", expected, rs) 809 } 810 } 811 812 func TestScannerWithContextCanceled(t *testing.T) { 813 ctrl := test.NewController(t) 814 defer ctrl.Finish() 815 c := mock.NewMockRPCClient(ctrl) 816 817 ctx, cancel := context.WithCancel(context.Background()) 818 scan, err := hrpc.NewScan(ctx, []byte(t.Name())) 819 if err != nil { 820 t.Fatal(err) 821 } 822 823 scanner := newScanner(c, scan, slog.Default()) 824 825 cancel() 826 827 _, err = scanner.Next() 828 if err != context.Canceled { 829 t.Fatalf("unexpected error %v, expected %v", err, context.Canceled) 830 } 831 } 832 833 func TestScannerClosed(t *testing.T) { 834 ctrl := test.NewController(t) 835 defer ctrl.Finish() 836 c := mock.NewMockRPCClient(ctrl) 837 838 scan, err := hrpc.NewScan(context.Background(), []byte(t.Name())) 839 if err != nil { 840 t.Fatal(err) 841 } 842 843 scanner := newScanner(c, scan, slog.Default()) 844 scanner.Close() 845 846 _, err = scanner.Next() 847 if err != io.EOF { 848 t.Fatalf("unexpected error %v, expected %v", err, io.EOF) 849 } 850 }