vitess.io/vitess@v0.16.2/go/vt/key/key_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package key 18 19 import ( 20 "encoding/hex" 21 "strings" 22 "testing" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 "google.golang.org/protobuf/proto" 27 28 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 29 ) 30 31 func TestKey(t *testing.T) { 32 k0 := Uint64Key(0) 33 k1 := Uint64Key(1) 34 k2 := Uint64Key(0x7FFFFFFFFFFFFFFF) 35 k3 := Uint64Key(0x8000000000000000) 36 k4 := Uint64Key(0xFFFFFFFFFFFFFFFF) 37 38 f := func(k Uint64Key, x string) { 39 hexK := hex.EncodeToString(k.Bytes()) 40 if x != hexK { 41 t.Errorf("byte mismatch %#v != %#v", k, x) 42 } 43 } 44 45 f(k0, "0000000000000000") 46 f(k1, "0000000000000001") 47 f(k2, "7fffffffffffffff") 48 f(k3, "8000000000000000") 49 f(k4, "ffffffffffffffff") 50 } 51 52 func TestEvenShardsKeyRange(t *testing.T) { 53 testCases := []struct { 54 i, n int 55 wantSpec string 56 want *topodatapb.KeyRange 57 }{ 58 {0, 1, 59 "-", 60 &topodatapb.KeyRange{}, 61 }, 62 {0, 2, 63 "-80", 64 &topodatapb.KeyRange{ 65 End: []byte{0x80}, 66 }, 67 }, 68 {1, 2, 69 "80-", 70 &topodatapb.KeyRange{ 71 Start: []byte{0x80}, 72 }, 73 }, 74 {1, 4, 75 "40-80", 76 &topodatapb.KeyRange{ 77 Start: []byte{0x40}, 78 End: []byte{0x80}, 79 }, 80 }, 81 {2, 4, 82 "80-c0", 83 &topodatapb.KeyRange{ 84 Start: []byte{0x80}, 85 End: []byte{0xc0}, 86 }, 87 }, 88 {1, 256, 89 "01-02", 90 &topodatapb.KeyRange{ 91 Start: []byte{0x01}, 92 End: []byte{0x02}, 93 }, 94 }, 95 {256, 512, 96 "8000-8080", 97 &topodatapb.KeyRange{ 98 Start: []byte{0x80, 0x00}, 99 End: []byte{0x80, 0x80}, 100 }, 101 }, 102 // Second to last shard out of 512. 103 {510, 512, 104 "ff00-ff80", 105 &topodatapb.KeyRange{ 106 Start: []byte{0xff, 0x00}, 107 End: []byte{0xff, 0x80}, 108 }, 109 }, 110 // Last out of 512 shards. 111 {511, 512, 112 "ff80-", 113 &topodatapb.KeyRange{ 114 Start: []byte{0xff, 0x80}, 115 }, 116 }, 117 } 118 119 for _, tc := range testCases { 120 got, err := EvenShardsKeyRange(tc.i, tc.n) 121 if err != nil { 122 t.Fatalf("EvenShardsKeyRange(%v, %v) returned unexpected error: %v", tc.i, tc.n, err) 123 } 124 if !proto.Equal(got, tc.want) { 125 t.Errorf("EvenShardsKeyRange(%v, %v) = (%x, %x), want = (%x, %x)", tc.i, tc.n, got.Start, got.End, tc.want.Start, tc.want.End) 126 } 127 128 // Check if the string representation is equal as well. 129 if gotStr, want := KeyRangeString(got), tc.wantSpec; gotStr != want { 130 t.Errorf("EvenShardsKeyRange(%v) = %v, want = %v", got, gotStr, want) 131 } 132 133 // Now verify that ParseKeyRangeParts() produces the same KeyRange object as 134 // we do. 135 parts := strings.Split(tc.wantSpec, "-") 136 kr, _ := ParseKeyRangeParts(parts[0], parts[1]) 137 if !proto.Equal(got, kr) { 138 t.Errorf("EvenShardsKeyRange(%v, %v) != ParseKeyRangeParts(%v, %v): (%x, %x) != (%x, %x)", tc.i, tc.n, parts[0], parts[1], got.Start, got.End, kr.Start, kr.End) 139 } 140 } 141 } 142 143 func TestKeyRangeAdd(t *testing.T) { 144 testcases := []struct { 145 first string 146 second string 147 out string 148 ok bool 149 }{{ 150 first: "", 151 second: "", 152 out: "", 153 ok: false, 154 }, { 155 first: "", 156 second: "-80", 157 out: "", 158 ok: false, 159 }, { 160 first: "-80", 161 second: "", 162 out: "", 163 ok: false, 164 }, { 165 first: "", 166 second: "80-", 167 out: "", 168 ok: false, 169 }, { 170 first: "80-", 171 second: "", 172 out: "", 173 ok: false, 174 }, { 175 first: "80-", 176 second: "-40", 177 out: "", 178 ok: false, 179 }, { 180 first: "-40", 181 second: "80-", 182 out: "", 183 ok: false, 184 }, { 185 first: "-80", 186 second: "80-", 187 out: "-", 188 ok: true, 189 }, { 190 first: "80-", 191 second: "-80", 192 out: "-", 193 ok: true, 194 }, { 195 first: "-40", 196 second: "40-80", 197 out: "-80", 198 ok: true, 199 }, { 200 first: "40-80", 201 second: "-40", 202 out: "-80", 203 ok: true, 204 }, { 205 first: "40-80", 206 second: "80-c0", 207 out: "40-c0", 208 ok: true, 209 }, { 210 first: "80-c0", 211 second: "40-80", 212 out: "40-c0", 213 ok: true, 214 }} 215 keyRangeToString := func(kr *topodatapb.KeyRange) string { 216 if kr == nil { 217 return "" 218 } 219 return KeyRangeString(kr) 220 } 221 for _, tcase := range testcases { 222 first := stringToKeyRange(tcase.first) 223 second := stringToKeyRange(tcase.second) 224 out, ok := KeyRangeAdd(first, second) 225 assert.Equal(t, tcase.out, keyRangeToString(out)) 226 assert.Equal(t, tcase.ok, ok) 227 } 228 } 229 230 func TestKeyRangeEndEqual(t *testing.T) { 231 testcases := []struct { 232 first string 233 second string 234 out bool 235 }{{ 236 first: "", 237 second: "", 238 out: true, 239 }, { 240 first: "", 241 second: "-80", 242 out: false, 243 }, { 244 first: "40-", 245 second: "10-", 246 out: true, 247 }, { 248 first: "-8000", 249 second: "-80", 250 out: true, 251 }, { 252 first: "-8000", 253 second: "-8000000000000000", 254 out: true, 255 }, { 256 first: "-80", 257 second: "-8000", 258 out: true, 259 }} 260 261 for _, tcase := range testcases { 262 first := stringToKeyRange(tcase.first) 263 second := stringToKeyRange(tcase.second) 264 out := KeyRangeEndEqual(first, second) 265 if out != tcase.out { 266 t.Fatalf("KeyRangeEndEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out) 267 } 268 } 269 } 270 271 func TestKeyRangeStartEqual(t *testing.T) { 272 testcases := []struct { 273 first string 274 second string 275 out bool 276 }{{ 277 first: "", 278 second: "", 279 out: true, 280 }, { 281 first: "", 282 second: "-80", 283 out: true, 284 }, { 285 first: "40-", 286 second: "20-", 287 out: false, 288 }, { 289 first: "-8000", 290 second: "-80", 291 out: true, 292 }, { 293 first: "-8000", 294 second: "-8000000000000000", 295 out: true, 296 }, { 297 first: "-80", 298 second: "-8000", 299 out: true, 300 }} 301 302 for _, tcase := range testcases { 303 first := stringToKeyRange(tcase.first) 304 second := stringToKeyRange(tcase.second) 305 out := KeyRangeStartEqual(first, second) 306 if out != tcase.out { 307 t.Fatalf("KeyRangeStartEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out) 308 } 309 } 310 } 311 312 func TestKeyRangeEqual(t *testing.T) { 313 testcases := []struct { 314 first string 315 second string 316 out bool 317 }{{ 318 first: "", 319 second: "", 320 out: true, 321 }, { 322 first: "", 323 second: "-80", 324 out: false, 325 }, { 326 first: "-8000", 327 second: "-80", 328 out: true, 329 }, { 330 first: "-8000", 331 second: "-8000000000000000", 332 out: true, 333 }, { 334 first: "-80", 335 second: "-8000", 336 out: true, 337 }} 338 339 for _, tcase := range testcases { 340 first := stringToKeyRange(tcase.first) 341 second := stringToKeyRange(tcase.second) 342 out := KeyRangeEqual(first, second) 343 if out != tcase.out { 344 t.Fatalf("KeyRangeEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out) 345 } 346 } 347 } 348 349 func TestKeyRangeContiguous(t *testing.T) { 350 testcases := []struct { 351 first string 352 second string 353 out bool 354 }{{ 355 first: "-40", 356 second: "40-80", 357 out: true, 358 }, { 359 first: "40-80", 360 second: "-40", 361 out: false, 362 }, { 363 first: "-", 364 second: "-40", 365 out: true, 366 }, { 367 first: "40-80", 368 second: "c0-", 369 out: false, 370 }, { 371 first: "40-80", 372 second: "80-c0", 373 out: true, 374 }, { 375 first: "40-80", 376 second: "8000000000000000-c000000000000000", 377 out: true, 378 }, { 379 first: "4000000000000000-8000000000000000", 380 second: "80-c0", 381 out: true, 382 }} 383 384 for _, tcase := range testcases { 385 first := stringToKeyRange(tcase.first) 386 second := stringToKeyRange(tcase.second) 387 out := KeyRangeContiguous(first, second) 388 if out != tcase.out { 389 t.Fatalf("KeyRangeContiguous(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out) 390 } 391 } 392 } 393 394 func TestEvenShardsKeyRange_Error(t *testing.T) { 395 testCases := []struct { 396 i, n int 397 wantError string 398 }{ 399 { 400 -1, 0, 401 "the shard count must be > 0", 402 }, 403 { 404 32, 8, 405 "must be less than", 406 }, 407 { 408 1, 6, 409 "must be a power of two", 410 }, 411 } 412 413 for _, tc := range testCases { 414 kr, err := EvenShardsKeyRange(tc.i, tc.n) 415 if err == nil || !strings.Contains(err.Error(), tc.wantError) { 416 t.Fatalf("EvenShardsKeyRange(%v, %v) = (%v, %v) want error = %v", tc.i, tc.n, kr, err, tc.wantError) 417 } 418 } 419 } 420 421 func TestParseShardingSpec(t *testing.T) { 422 x40 := []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 423 x80 := []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 424 goodTable := map[string][]*topodatapb.KeyRange{ 425 "-": {{}}, 426 "0": {{}}, 427 "-4000000000000000-8000000000000000-": { 428 {End: x40}, 429 {Start: x40, End: x80}, 430 {Start: x80}, 431 }, 432 } 433 badTable := []string{ 434 "4000000000000000", 435 "---", 436 "4000000000000000--8000000000000000", 437 "4000000000000000-3000000000000000", // not in order 438 } 439 for key, wanted := range goodTable { 440 r, err := ParseShardingSpec(key) 441 if err != nil { 442 t.Errorf("Unexpected error: %v.", err) 443 } 444 if len(r) != len(wanted) { 445 t.Errorf("Wrong result: wanted %v, got %v", wanted, r) 446 continue 447 } 448 for i, w := range wanted { 449 if !proto.Equal(r[i], w) { 450 t.Errorf("Wrong result: wanted %v, got %v", w, r[i]) 451 break 452 } 453 } 454 } 455 for _, bad := range badTable { 456 _, err := ParseShardingSpec(bad) 457 if err == nil { 458 t.Errorf("Didn't get expected error for %v.", bad) 459 } 460 } 461 } 462 463 func TestContains(t *testing.T) { 464 var table = []struct { 465 kid string 466 start string 467 end string 468 contained bool 469 }{ 470 {kid: "3000000000000000", start: "3000000000000000", end: "", contained: true}, 471 {kid: "3000000000000000", start: "", end: "3000000000000000", contained: false}, 472 {kid: "4000000000000000", start: "3000000000000000", end: "", contained: true}, 473 {kid: "2000000000000000", start: "3000000000000000", end: "", contained: false}, 474 } 475 476 for _, el := range table { 477 s, err := hex.DecodeString(el.start) 478 if err != nil { 479 t.Errorf("Unexpected error: %v", err) 480 } 481 e, err := hex.DecodeString(el.end) 482 if err != nil { 483 t.Errorf("Unexpected error: %v", err) 484 } 485 kr := &topodatapb.KeyRange{ 486 Start: s, 487 End: e, 488 } 489 k, err := hex.DecodeString(el.kid) 490 if err != nil { 491 t.Errorf("Unexpected error: %v", err) 492 } 493 if c := KeyRangeContains(kr, k); c != el.contained { 494 t.Errorf("Unexpected result: contains for %v and (%v-%v) yields %v.", el.kid, el.start, el.end, c) 495 } 496 if !KeyRangeContains(nil, k) { 497 t.Errorf("KeyRangeContains(nil, x) should always be true") 498 } 499 } 500 } 501 502 func TestIntersectOverlap(t *testing.T) { 503 var table = []struct { 504 a string 505 b string 506 c string 507 d string 508 intersects bool 509 overlap string 510 }{ 511 {a: "40", b: "80", c: "c0", d: "d0", intersects: false}, 512 {a: "", b: "80", c: "80", d: "", intersects: false}, 513 {a: "", b: "80", c: "", d: "40", intersects: true, overlap: "-40"}, 514 {a: "80", b: "", c: "c0", d: "", intersects: true, overlap: "c0-"}, 515 {a: "", b: "80", c: "40", d: "80", intersects: true, overlap: "40-80"}, 516 {a: "40", b: "80", c: "60", d: "a0", intersects: true, overlap: "60-80"}, 517 {a: "40", b: "80", c: "50", d: "60", intersects: true, overlap: "50-60"}, 518 {a: "40", b: "80", c: "10", d: "50", intersects: true, overlap: "40-50"}, 519 {a: "40", b: "80", c: "40", d: "80", intersects: true, overlap: "40-80"}, 520 {a: "", b: "80", c: "", d: "80", intersects: true, overlap: "-80"}, 521 {a: "40", b: "", c: "40", d: "", intersects: true, overlap: "40-"}, 522 {a: "40", b: "80", c: "20", d: "40", intersects: false}, 523 {a: "80", b: "", c: "80", d: "c0", intersects: true, overlap: "80-c0"}, 524 {a: "", b: "", c: "c0", d: "d0", intersects: true, overlap: "c0-d0"}, 525 } 526 527 for _, el := range table { 528 a, err := hex.DecodeString(el.a) 529 if err != nil { 530 t.Errorf("Unexpected error: %v", err) 531 } 532 b, err := hex.DecodeString(el.b) 533 if err != nil { 534 t.Errorf("Unexpected error: %v", err) 535 } 536 left := &topodatapb.KeyRange{Start: a, End: b} 537 c, err := hex.DecodeString(el.c) 538 if err != nil { 539 t.Errorf("Unexpected error: %v", err) 540 } 541 d, err := hex.DecodeString(el.d) 542 if err != nil { 543 t.Errorf("Unexpected error: %v", err) 544 } 545 right := &topodatapb.KeyRange{Start: c, End: d} 546 if c := KeyRangesIntersect(left, right); c != el.intersects { 547 t.Errorf("Unexpected result: KeyRangesIntersect for %v and %v yields %v.", left, right, c) 548 } 549 overlap, err := KeyRangesOverlap(left, right) 550 if el.intersects { 551 if err != nil { 552 t.Errorf("Unexpected result: KeyRangesOverlap for overlapping %v and %v returned an error: %v", left, right, err) 553 } else { 554 got := hex.EncodeToString(overlap.Start) + "-" + hex.EncodeToString(overlap.End) 555 if got != el.overlap { 556 t.Errorf("Unexpected result: KeyRangesOverlap for overlapping %v and %v should have returned: %v but got: %v", left, right, el.overlap, got) 557 } 558 } 559 } else { 560 if err == nil { 561 t.Errorf("Unexpected result: KeyRangesOverlap for non-overlapping %v and %v should have returned an error", left, right) 562 } 563 } 564 } 565 } 566 567 func TestKeyRangeIncludes(t *testing.T) { 568 var table = []struct { 569 name string 570 big string 571 small string 572 expected bool 573 }{ 574 {"big nil, small nil", "nil", "nil", true}, 575 {"big nil, small non nil, fully partial", "nil", "80-c0", true}, 576 {"big nil, small non nil, full start", "nil", "-c0", true}, 577 {"big nil, small non nil, full end", "nil", "80-", true}, 578 {"big non-nil, fully partial, small nil", "80-c0", "nil", false}, 579 {"big non-nil, full start, small nil", "-c0", "nil", false}, 580 {"big non-nil, full end, small nil", "80-", "nil", false}, 581 {"big full, small full", "-", "-", true}, 582 {"big full, small partial", "-", "40-60", true}, 583 {"big partial, small full", "40-60", "-", false}, 584 585 {"big partial, small to the end", "40-60", "40-", false}, 586 {"big partial, small bigger to the right", "40-60", "40-80", false}, 587 {"big partial, small equal", "40-60", "40-60", true}, 588 {"big partial, small smaller right", "40-60", "40-50", true}, 589 590 {"big partial, small to the beginning", "40-60", "-60", false}, 591 {"big partial, small smaller to the left", "40-60", "20-60", false}, 592 {"big partial, small bigger left", "40-60", "50-60", true}, 593 } 594 595 var err error 596 for _, tc := range table { 597 var big, small *topodatapb.KeyRange 598 if tc.big != "nil" { 599 parts := strings.Split(tc.big, "-") 600 big, err = ParseKeyRangeParts(parts[0], parts[1]) 601 if err != nil { 602 t.Fatalf("test data error in %v: %v", tc.big, err) 603 } 604 } 605 if tc.small != "nil" { 606 parts := strings.Split(tc.small, "-") 607 small, err = ParseKeyRangeParts(parts[0], parts[1]) 608 if err != nil { 609 t.Fatalf("test data error in %v: %v", tc.small, err) 610 } 611 } 612 got := KeyRangeIncludes(big, small) 613 if got != tc.expected { 614 t.Errorf("KeyRangeIncludes for test case '%v' returned %v but expected %v", tc.name, got, tc.expected) 615 } 616 } 617 } 618 619 func BenchmarkUint64KeyBytes(b *testing.B) { 620 keys := []Uint64Key{ 621 0, 1, 0x7FFFFFFFFFFFFFFF, 0x8000000000000000, 0xFFFFFFFFFFFFFFFF, 622 } 623 624 for i := 0; i < b.N; i++ { 625 for _, key := range keys { 626 key.Bytes() 627 } 628 } 629 } 630 631 func BenchmarkUint64KeyString(b *testing.B) { 632 keys := []Uint64Key{ 633 0, 1, 0x7FFFFFFFFFFFFFFF, 0x8000000000000000, 0xFFFFFFFFFFFFFFFF, 634 } 635 636 for i := 0; i < b.N; i++ { 637 for _, key := range keys { 638 _ = key.String() 639 } 640 } 641 } 642 643 func BenchmarkKeyRangeContains(b *testing.B) { 644 kr := &topodatapb.KeyRange{ 645 Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0}, 646 End: []byte{0x80, 0, 0, 0, 0, 0, 0, 0}, 647 } 648 keys := [][]byte{ 649 {0x30, 0, 0, 0, 0, 0, 0, 0}, 650 {0x40, 0, 0, 0, 0, 0, 0, 0}, 651 {0x50, 0, 0, 0, 0, 0, 0, 0}, 652 {0x80, 0, 0, 0, 0, 0, 0, 0}, 653 {0x90, 0, 0, 0, 0, 0, 0, 0}, 654 } 655 656 for i := 0; i < b.N; i++ { 657 for _, key := range keys { 658 KeyRangeContains(kr, key) 659 } 660 } 661 } 662 663 func BenchmarkKeyRangesIntersect(b *testing.B) { 664 kr1 := &topodatapb.KeyRange{ 665 Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0}, 666 End: []byte{0x80, 0, 0, 0, 0, 0, 0, 0}, 667 } 668 kr2 := &topodatapb.KeyRange{ 669 Start: []byte{0x30, 0, 0, 0, 0, 0, 0, 0}, 670 End: []byte{0x50, 0, 0, 0, 0, 0, 0, 0}, 671 } 672 673 for i := 0; i < b.N; i++ { 674 KeyRangesIntersect(kr1, kr2) 675 } 676 } 677 678 func BenchmarkKeyRangesOverlap(b *testing.B) { 679 kr1 := &topodatapb.KeyRange{ 680 Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0}, 681 End: []byte{0x80, 0, 0, 0, 0, 0, 0, 0}, 682 } 683 kr2 := &topodatapb.KeyRange{ 684 Start: []byte{0x30, 0, 0, 0, 0, 0, 0, 0}, 685 End: []byte{0x50, 0, 0, 0, 0, 0, 0, 0}, 686 } 687 688 for i := 0; i < b.N; i++ { 689 if _, err := KeyRangesOverlap(kr1, kr2); err != nil { 690 b.Fatal(err) 691 } 692 } 693 } 694 695 func TestIsKeyRange(t *testing.T) { 696 testcases := []struct { 697 in string 698 out bool 699 }{{ 700 in: "-", 701 out: true, 702 }, { 703 in: "-80", 704 out: true, 705 }, { 706 in: "40-80", 707 out: true, 708 }, { 709 in: "80-", 710 out: true, 711 }, { 712 in: "a0-", 713 out: true, 714 }, { 715 in: "-A0", 716 out: true, 717 }, { 718 in: "", 719 out: false, 720 }, { 721 in: "x-80", 722 out: false, 723 }, { 724 in: "-80x", 725 out: false, 726 }, { 727 in: "select", 728 out: false, 729 }} 730 731 for _, tcase := range testcases { 732 assert.Equal(t, IsKeyRange(tcase.in), tcase.out, tcase.in) 733 } 734 } 735 736 func TestGenerateShardRanges(t *testing.T) { 737 type args struct { 738 shards int 739 } 740 741 tests := []struct { 742 name string 743 args args 744 want []string 745 wantErr bool 746 }{ 747 { 748 "errors for shards less than 0", 749 args{0}, 750 nil, 751 true, 752 }, 753 { 754 "errors for shards more than 65536", 755 args{65537}, 756 nil, 757 true, 758 }, 759 { 760 "works for a single shard", 761 args{1}, 762 []string{"-"}, 763 false, 764 }, 765 { 766 "works for more than one shard", 767 args{2}, 768 []string{"-80", "80-"}, 769 false, 770 }, 771 { 772 "works for an odd number of shards", 773 args{7}, 774 []string{"-24", "24-49", "49-6d", "6d-92", "92-b6", "b6-db", "db-"}, 775 false, 776 }, 777 { 778 "works for large number of shards", 779 args{256}, 780 []string{"-01", "01-02", "02-03", "03-04", "04-05", "05-06", "06-07", "07-08", "08-09", "09-0a", "0a-0b", "0b-0c", "0c-0d", "0d-0e", "0e-0f", "0f-10", "10-11", "11-12", "12-13", "13-14", "14-15", "15-16", "16-17", "17-18", "18-19", "19-1a", "1a-1b", "1b-1c", "1c-1d", "1d-1e", "1e-1f", "1f-20", "20-21", "21-22", "22-23", "23-24", "24-25", "25-26", "26-27", "27-28", "28-29", "29-2a", "2a-2b", "2b-2c", "2c-2d", "2d-2e", "2e-2f", "2f-30", "30-31", "31-32", "32-33", "33-34", "34-35", "35-36", "36-37", "37-38", "38-39", "39-3a", "3a-3b", "3b-3c", "3c-3d", "3d-3e", "3e-3f", "3f-40", "40-41", "41-42", "42-43", "43-44", "44-45", "45-46", "46-47", "47-48", "48-49", "49-4a", "4a-4b", "4b-4c", "4c-4d", "4d-4e", "4e-4f", "4f-50", "50-51", "51-52", "52-53", "53-54", "54-55", "55-56", "56-57", "57-58", "58-59", "59-5a", "5a-5b", "5b-5c", "5c-5d", "5d-5e", "5e-5f", "5f-60", "60-61", "61-62", "62-63", "63-64", "64-65", "65-66", "66-67", "67-68", "68-69", "69-6a", "6a-6b", "6b-6c", "6c-6d", "6d-6e", "6e-6f", "6f-70", "70-71", "71-72", "72-73", "73-74", "74-75", "75-76", "76-77", "77-78", "78-79", "79-7a", "7a-7b", "7b-7c", "7c-7d", "7d-7e", "7e-7f", "7f-80", "80-81", "81-82", "82-83", "83-84", "84-85", "85-86", "86-87", "87-88", "88-89", "89-8a", "8a-8b", "8b-8c", "8c-8d", "8d-8e", "8e-8f", "8f-90", "90-91", "91-92", "92-93", "93-94", "94-95", "95-96", "96-97", "97-98", "98-99", "99-9a", "9a-9b", "9b-9c", "9c-9d", "9d-9e", "9e-9f", "9f-a0", "a0-a1", "a1-a2", "a2-a3", "a3-a4", "a4-a5", "a5-a6", "a6-a7", "a7-a8", "a8-a9", "a9-aa", "aa-ab", "ab-ac", "ac-ad", "ad-ae", "ae-af", "af-b0", "b0-b1", "b1-b2", "b2-b3", "b3-b4", "b4-b5", "b5-b6", "b6-b7", "b7-b8", "b8-b9", "b9-ba", "ba-bb", "bb-bc", "bc-bd", "bd-be", "be-bf", "bf-c0", "c0-c1", "c1-c2", "c2-c3", "c3-c4", "c4-c5", "c5-c6", "c6-c7", "c7-c8", "c8-c9", "c9-ca", "ca-cb", "cb-cc", "cc-cd", "cd-ce", "ce-cf", "cf-d0", "d0-d1", "d1-d2", "d2-d3", "d3-d4", "d4-d5", "d5-d6", "d6-d7", "d7-d8", "d8-d9", "d9-da", "da-db", "db-dc", "dc-dd", "dd-de", "de-df", "df-e0", "e0-e1", "e1-e2", "e2-e3", "e3-e4", "e4-e5", "e5-e6", "e6-e7", "e7-e8", "e8-e9", "e9-ea", "ea-eb", "eb-ec", "ec-ed", "ed-ee", "ee-ef", "ef-f0", "f0-f1", "f1-f2", "f2-f3", "f3-f4", "f4-f5", "f5-f6", "f6-f7", "f7-f8", "f8-f9", "f9-fa", "fa-fb", "fb-fc", "fc-fd", "fd-fe", "fe-ff", "ff-"}, 781 false, 782 }, 783 } 784 785 for _, tt := range tests { 786 t.Run(tt.name, func(t *testing.T) { 787 got, err := GenerateShardRanges(tt.args.shards) 788 if tt.wantErr { 789 assert.Error(t, err) 790 return 791 } 792 793 require.NoError(t, err) 794 assert.Equal(t, got, tt.want) 795 }) 796 } 797 } 798 799 func TestShardCalculatorForShardsGreaterThan512(t *testing.T) { 800 got, err := GenerateShardRanges(512) 801 assert.NoError(t, err) 802 803 want := "ff80-" 804 805 assert.Equal(t, want, got[511], "Invalid mapping for a 512-shard keyspace. Expected %v, got %v", want, got[511]) 806 } 807 808 func stringToKeyRange(spec string) *topodatapb.KeyRange { 809 if spec == "" { 810 return nil 811 } 812 parts := strings.Split(spec, "-") 813 if len(parts) != 2 { 814 panic("invalid spec") 815 } 816 kr, err := ParseKeyRangeParts(parts[0], parts[1]) 817 if err != nil { 818 panic(err) 819 } 820 return kr 821 }