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  }