github.com/dubbogo/gost@v1.14.0/hash/consistent/consistent_test.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package consistent 19 20 import ( 21 "sort" 22 "testing" 23 "testing/quick" 24 ) 25 26 import ( 27 "github.com/spaolacci/murmur3" 28 ) 29 30 func murmurHash(key []byte) uint64 { 31 return murmur3.Sum64WithSeed(key, 3238918481) 32 } 33 34 func checkNum(num, expected int, t *testing.T) { 35 if num != expected { 36 t.Errorf("got %d, expected %d", num, expected) 37 } 38 } 39 40 func TestNewConsistentHash(t *testing.T) { 41 c := NewConsistentHash(WithReplicaNum(13), WithMaxVnodeNum(1023)) 42 if c == nil { 43 t.Fatal("expected obj") 44 } 45 checkNum(int(c.replicaFactor), 13, t) 46 } 47 48 func TestAdd(t *testing.T) { 49 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 50 51 c.Add("127.0.0.1:8000") 52 if len(c.sortedHashes) != replicationFactor { 53 t.Fatal("vnodes number is incorrect") 54 } 55 56 checkNum(len(c.circle), 10, t) 57 checkNum(len(c.sortedHashes), 10, t) 58 if sort.IsSorted(c.sortedHashes) == false { 59 t.Error("expected sorted hashes to be sorted") 60 } 61 62 c.Add("qwer") 63 checkNum(len(c.circle), 20, t) 64 checkNum(len(c.sortedHashes), 20, t) 65 if sort.IsSorted(c.sortedHashes) == false { 66 t.Error("expected sorted hashes to be sorted") 67 } 68 } 69 70 func TestGet(t *testing.T) { 71 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 72 73 c.Add("127.0.0.1:8000") 74 host, err := c.Get("127.0.0.1:8000") 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 if host != "127.0.0.1:8000" { 80 t.Fatal("returned host is not what expected") 81 } 82 } 83 84 func TestGetHash(t *testing.T) { 85 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 86 87 c.Add("127.0.0.1:8000") 88 host, err := c.GetHash(123) 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 if host != "127.0.0.1:8000" { 94 t.Fatal("returned host is not what expected") 95 } 96 } 97 98 func TestGetEmpty(t *testing.T) { 99 c := NewConsistentHash(WithReplicaNum(13), WithMaxVnodeNum(1023)) 100 _, err := c.Get("asdfsadfsadf") 101 if err == nil { 102 t.Error("expected error") 103 } 104 if err != ErrNoHosts { 105 t.Error("expected empty circle error") 106 } 107 } 108 109 func TestGetSingle(t *testing.T) { 110 c := NewConsistentHash(WithReplicaNum(13), WithMaxVnodeNum(1023)) 111 c.Add("abcdefg") 112 f := func(s string) bool { 113 y, err := c.Get(s) 114 if err != nil { 115 t.Log("error: ", err) 116 return false 117 } 118 // t.Logf("s = %q, y = %q", s, y) 119 return y == "abcdefg" 120 } 121 if err := quick.Check(f, nil); err != nil { 122 t.Fatal(err) 123 } 124 } 125 126 type gtest struct { 127 in string 128 out string 129 } 130 131 var gmtests = []gtest{ 132 {"ggg", "opqrstu"}, 133 {"hhh", "abcdefg"}, 134 {"iiiii", "hijklmn"}, 135 } 136 137 func TestGetMultiple(t *testing.T) { 138 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 139 c.Add("abcdefg") 140 c.Add("hijklmn") 141 c.Add("opqrstu") 142 for i, v := range gmtests { 143 result, err := c.Get(v.in) 144 if err != nil { 145 t.Fatal(err) 146 } 147 if result != v.out { 148 t.Errorf("%d. got %q, expected %q", i, result, v.out) 149 } 150 } 151 } 152 153 func TestGetMultipleQuick(t *testing.T) { 154 c := NewConsistentHash(WithReplicaNum(13), WithMaxVnodeNum(1023)) 155 c.Add("abcdefg") 156 c.Add("hijklmn") 157 c.Add("opqrstu") 158 f := func(s string) bool { 159 y, err := c.Get(s) 160 if err != nil { 161 t.Log("error: ", err) 162 return false 163 } 164 // t.Logf("s = %q, y = %q", s, y) 165 return y == "abcdefg" || y == "hijklmn" || y == "opqrstu" 166 } 167 if err := quick.Check(f, nil); err != nil { 168 t.Fatal(err) 169 } 170 } 171 172 var rtestsBefore = []gtest{ 173 {"ggg", "abcdefg"}, 174 {"hhh", "abcdefg"}, 175 {"iiiii", "opqrstu"}, 176 } 177 178 var rtestsAfter = []gtest{ 179 {"ggg", "abcdefg"}, 180 {"hhh", "abcdefg"}, 181 {"iiiii", "opqrstu"}, 182 } 183 184 func TestGetMultipleRemove(t *testing.T) { 185 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 186 c.Add("abcdefg") 187 c.Add("hijklmn") 188 c.Add("opqrstu") 189 for i, v := range rtestsBefore { 190 result, err := c.Get(v.in) 191 if err != nil { 192 t.Fatal(err) 193 } 194 if result != v.out { 195 t.Errorf("%d. got %q, expected %q before rm", i, result, v.out) 196 } 197 } 198 c.Remove("hijklmn") 199 for i, v := range rtestsAfter { 200 result, err := c.Get(v.in) 201 if err != nil { 202 t.Fatal(err) 203 } 204 if result != v.out { 205 t.Errorf("%d. got %q, expected %q after rm", i, result, v.out) 206 } 207 } 208 } 209 210 func TestGetMultipleRemoveQuick(t *testing.T) { 211 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 212 c.Add("abcdefg") 213 c.Add("hijklmn") 214 c.Add("opqrstu") 215 c.Remove("opqrstu") 216 f := func(s string) bool { 217 y, err := c.Get(s) 218 if err != nil { 219 t.Log("error: ", err) 220 return false 221 } 222 // t.Logf("s = %q, y = %q", s, y) 223 return y == "abcdefg" || y == "hijklmn" 224 } 225 if err := quick.Check(f, nil); err != nil { 226 t.Fatal(err) 227 } 228 } 229 func TestGetTwo(t *testing.T) { 230 c := NewConsistentHash(WithReplicaNum(13), WithMaxVnodeNum(10230)) 231 c.Add("abcdefg") 232 c.Add("hijklmn") 233 c.Add("opqrstu") 234 a, b, err := c.GetTwo("99999999") 235 if err != nil { 236 t.Fatal(err) 237 } 238 if a == b { 239 t.Error("a shouldn't equal b") 240 } 241 if a != "opqrstu" { 242 t.Error("wrong a: ", a) 243 } 244 if b != "abcdefg" { 245 t.Error("wrong b: ", b) 246 } 247 } 248 249 func TestGetTwoQuick(t *testing.T) { 250 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 251 c.Add("abcdefg") 252 c.Add("hijklmn") 253 c.Add("opqrstu") 254 f := func(s string) bool { 255 a, b, err := c.GetTwo(s) 256 if err != nil { 257 t.Log("error: ", err) 258 return false 259 } 260 if a == b { 261 t.Log("a == b") 262 return false 263 } 264 if a != "abcdefg" && a != "hijklmn" && a != "opqrstu" { 265 t.Log("invalid a: ", a) 266 return false 267 } 268 269 if b != "abcdefg" && b != "hijklmn" && b != "opqrstu" { 270 t.Log("invalid b: ", b) 271 return false 272 } 273 return true 274 } 275 if err := quick.Check(f, nil); err != nil { 276 t.Fatal(err) 277 } 278 } 279 280 func TestGetTwoOnlyTwoQuick(t *testing.T) { 281 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 282 c.Add("abcdefg") 283 c.Add("hijklmn") 284 f := func(s string) bool { 285 a, b, err := c.GetTwo(s) 286 if err != nil { 287 t.Log("error: ", err) 288 return false 289 } 290 if a == b { 291 t.Log("a == b") 292 return false 293 } 294 if a != "abcdefg" && a != "hijklmn" { 295 t.Log("invalid a: ", a) 296 return false 297 } 298 299 if b != "abcdefg" && b != "hijklmn" { 300 t.Log("invalid b: ", b) 301 return false 302 } 303 return true 304 } 305 if err := quick.Check(f, nil); err != nil { 306 t.Fatal(err) 307 } 308 } 309 310 func TestGetTwoOnlyOneInCircle(t *testing.T) { 311 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 312 c.Add("abcdefg") 313 a, b, err := c.GetTwo("99999999") 314 if err != nil { 315 t.Fatal(err) 316 } 317 if a == b { 318 t.Error("a shouldn't equal b") 319 } 320 if a != "abcdefg" { 321 t.Error("wrong a: ", a) 322 } 323 if b != "" { 324 t.Error("wrong b: ", b) 325 } 326 } 327 328 func TestGetN(t *testing.T) { 329 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 330 c.Add("abcdefg") 331 c.Add("hijklmn") 332 c.Add("opqrstu") 333 members, err := c.GetN("9999999", 3) 334 if err != nil { 335 t.Fatal(err) 336 } 337 if len(members) != 3 { 338 t.Error("expected 3 members instead of ", len(members)) 339 } 340 if members[0] != "abcdefg" { 341 t.Error("wrong members[0]: ", members[0]) 342 } 343 if members[1] != "hijklmn" { 344 t.Error("wrong members[1]: ", members[1]) 345 } 346 if members[2] != "opqrstu" { 347 t.Error("wrong members[2]: ", members[2]) 348 } 349 } 350 351 func TestGetNLess(t *testing.T) { 352 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 353 c.Add("abcdefg") 354 c.Add("hijklmn") 355 c.Add("opqrstu") 356 members, err := c.GetN("99999999", 2) 357 if err != nil { 358 t.Fatal(err) 359 } 360 if len(members) != 2 { 361 t.Error("expected 2 members instead of ", len(members)) 362 } 363 if members[0] != "abcdefg" { 364 t.Error("wrong members[0]: ", members[0]) 365 } 366 if members[1] != "hijklmn" { 367 t.Error("wrong members[1]: ", members[1]) 368 } 369 } 370 371 func TestGetNMore(t *testing.T) { 372 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 373 c.Add("abcdefg") 374 c.Add("hijklmn") 375 c.Add("opqrstu") 376 members, err := c.GetN("9999999", 5) 377 if err != nil { 378 t.Fatal(err) 379 } 380 if len(members) != 3 { 381 t.Error("expected 3 members instead of ", len(members)) 382 } 383 if members[0] != "abcdefg" { 384 t.Error("wrong members[0]: ", members[0]) 385 } 386 if members[1] != "hijklmn" { 387 t.Error("wrong members[1]: ", members[1]) 388 } 389 if members[2] != "opqrstu" { 390 t.Error("wrong members[2]: ", members[2]) 391 } 392 } 393 394 func TestGetNQuick(t *testing.T) { 395 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 396 c.Add("abcdefg") 397 c.Add("hijklmn") 398 c.Add("opqrstu") 399 f := func(s string) bool { 400 members, err := c.GetN(s, 3) 401 if err != nil { 402 t.Log("error: ", err) 403 return false 404 } 405 if len(members) != 3 { 406 t.Log("expected 3 members instead of ", len(members)) 407 return false 408 } 409 set := make(map[string]bool, 4) 410 for _, member := range members { 411 if set[member] { 412 t.Log("duplicate error") 413 return false 414 } 415 set[member] = true 416 if member != "abcdefg" && member != "hijklmn" && member != "opqrstu" { 417 t.Log("invalid member: ", member) 418 return false 419 } 420 } 421 return true 422 } 423 if err := quick.Check(f, nil); err != nil { 424 t.Fatal(err) 425 } 426 } 427 428 func TestGetNLessQuick(t *testing.T) { 429 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 430 c.Add("abcdefg") 431 c.Add("hijklmn") 432 c.Add("opqrstu") 433 f := func(s string) bool { 434 members, err := c.GetN(s, 2) 435 if err != nil { 436 t.Log("error: ", err) 437 return false 438 } 439 if len(members) != 2 { 440 t.Log("expected 2 members instead of ", len(members)) 441 return false 442 } 443 set := make(map[string]bool, 4) 444 for _, member := range members { 445 if set[member] { 446 t.Log("duplicate error") 447 return false 448 } 449 set[member] = true 450 if member != "abcdefg" && member != "hijklmn" && member != "opqrstu" { 451 t.Log("invalid member: ", member) 452 return false 453 } 454 } 455 return true 456 } 457 if err := quick.Check(f, nil); err != nil { 458 t.Fatal(err) 459 } 460 } 461 462 func TestGetNMoreQuick(t *testing.T) { 463 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 464 c.Add("abcdefg") 465 c.Add("hijklmn") 466 c.Add("opqrstu") 467 f := func(s string) bool { 468 members, err := c.GetN(s, 5) 469 if err != nil { 470 t.Log("error: ", err) 471 return false 472 } 473 if len(members) != 3 { 474 t.Log("expected 3 members instead of ", len(members)) 475 return false 476 } 477 set := make(map[string]bool, 4) 478 for _, member := range members { 479 if set[member] { 480 t.Log("duplicate error") 481 return false 482 } 483 set[member] = true 484 if member != "abcdefg" && member != "hijklmn" && member != "opqrstu" { 485 t.Log("invalid member: ", member) 486 return false 487 } 488 } 489 return true 490 } 491 if err := quick.Check(f, nil); err != nil { 492 t.Fatal(err) 493 } 494 } 495 496 func TestSet(t *testing.T) { 497 c := NewConsistentHash(WithReplicaNum(20), WithMaxVnodeNum(1023), WithHashFunc(murmurHash)) 498 c.Add("abc") 499 c.Add("def") 500 c.Add("ghi") 501 c.Set([]string{"jkl", "mno"}) 502 if len(c.loadMap) != 2 { 503 t.Errorf("expected 2 elts, got %d", len(c.loadMap)) 504 } 505 a, b, err := c.GetTwo("qwerqwerwqer") 506 if err != nil { 507 t.Fatal(err) 508 } 509 if a != "jkl" && a != "mno" { 510 t.Error("expected jkl or mno, got ", a) 511 } 512 if b != "jkl" && b != "mno" { 513 t.Error("expected jkl or mno, got ", b) 514 } 515 if a == b { 516 t.Error("expected a != b, they were both ", a) 517 } 518 c.Set([]string{"pqr", "mno"}) 519 if len(c.loadMap) != 2 { 520 t.Error("expected 2 elts, got ", len(c.loadMap)) 521 } 522 a, b, err = c.GetTwo("qwerqwerwqer") 523 if err != nil { 524 t.Fatal(err) 525 } 526 if a != "pqr" && a != "mno" { 527 t.Error("expected jkl or mno, got ", a) 528 } 529 if b != "pqr" && b != "mno" { 530 t.Error("expected jkl or mno, got ", b) 531 } 532 if a == b { 533 t.Error("expected a != b, they were both ", a) 534 } 535 c.Set([]string{"pqr", "mno"}) 536 if len(c.loadMap) != 2 { 537 t.Error("expected 2 elts, got ", len(c.loadMap)) 538 } 539 a, b, err = c.GetTwo("qwerqwerwqer") 540 if err != nil { 541 t.Fatal(err) 542 } 543 if a != "pqr" && a != "mno" { 544 t.Error("expected jkl or mno, got ", a) 545 } 546 if b != "pqr" && b != "mno" { 547 t.Error("expected jkl or mno, got ", b) 548 } 549 if a == b { 550 t.Error("expected a != b, they were both ", a) 551 } 552 } 553 func TestRemove(t *testing.T) { 554 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 555 556 c.Add("127.0.0.1:8000") 557 c.Remove("127.0.0.1:8000") 558 559 // if len(c.sortedHashes) != 0 && len(c.circle) != 0 { 560 if c.sortedHashes.Len() != 0 && len(c.circle) != 0 && c.totalLoad == 0 { 561 t.Fatal("remove is not working") 562 } 563 564 } 565 566 func TestRemoveNonExisting(t *testing.T) { 567 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 568 c.Add("abcdefg") 569 c.Remove("abcdefghijk") 570 checkNum(len(c.circle), 10, t) 571 } 572 573 func TestGetLeast(t *testing.T) { 574 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 575 576 c.Add("127.0.0.1:8000") 577 c.Add("92.0.0.1:8000") 578 579 for i := 0; i < 100; i++ { 580 host, err := c.GetLeast("92.0.0.1:80001") 581 if err != nil { 582 t.Fatal(err) 583 } 584 c.Inc(host) 585 } 586 587 for k, v := range c.GetLoads() { 588 if v > c.MaxLoad() { 589 t.Fatalf("host %s is overloaded. %d > %d\n", k, v, c.MaxLoad()) 590 } 591 } 592 t.Log("Max load per node", c.MaxLoad()) 593 t.Log(c.GetLoads()) 594 } 595 596 func TestIncDone(t *testing.T) { 597 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 598 599 c.Add("127.0.0.1:8000") 600 c.Add("92.0.0.1:8000") 601 602 host, err := c.GetLeast("92.0.0.1:80001") 603 if err != nil { 604 t.Fatal(err) 605 } 606 607 c.Inc(host) 608 if c.loadMap[host].Load != 1 { 609 t.Fatalf("host %s load should be 1\n", host) 610 } 611 612 c.Done(host) 613 if c.loadMap[host].Load != 0 { 614 t.Fatalf("host %s load should be 0\n", host) 615 } 616 617 } 618 619 func TestHosts(t *testing.T) { 620 hosts := []string{ 621 "127.0.0.1:8000", 622 "92.0.0.1:8000", 623 } 624 625 c := NewConsistentHash(WithReplicaNum(10), WithMaxVnodeNum(1023)) 626 for _, h := range hosts { 627 c.Add(h) 628 } 629 t.Log("hosts in the ring", c.Hosts()) 630 631 addedHosts := c.Hosts() 632 for _, h := range hosts { 633 found := false 634 for _, ah := range addedHosts { 635 if h == ah { 636 found = true 637 break 638 } 639 } 640 if !found { 641 t.Fatal("missing host", h) 642 } 643 } 644 c.Remove("127.0.0.1:8000") 645 t.Log("hosts in the ring", c.Hosts()) 646 } 647 648 func TestDelSlice(t *testing.T) { 649 items := []uint32{0, 1, 2, 3, 5, 20, 22, 23, 25, 27, 28, 30, 35, 37, 1008, 1009} 650 deletes := []uint32{25, 37, 1009, 3, 100000} 651 652 c := &Consistent{} 653 c.sortedHashes = append(c.sortedHashes, items...) 654 655 t.Logf("before deletion%+v\n", c.sortedHashes) 656 657 for _, val := range deletes { 658 c.delSlice(val) 659 } 660 661 for _, val := range deletes { 662 for _, item := range c.sortedHashes { 663 if item == val { 664 t.Fatalf("%d wasn't deleted\n", val) 665 } 666 } 667 } 668 669 t.Logf("after deletions: %+v\n", c.sortedHashes) 670 }