github.com/thanos-io/thanos@v0.32.5/pkg/cacheutil/memcached_server_selector_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package cacheutil 5 6 import ( 7 "fmt" 8 "net" 9 "testing" 10 11 "github.com/bradfitz/gomemcache/memcache" 12 "github.com/facette/natsort" 13 "github.com/pkg/errors" 14 15 "github.com/efficientgo/core/testutil" 16 ) 17 18 func TestNatSort(t *testing.T) { 19 20 // Validate that the order of SRV records returned by a DNS 21 // lookup for a k8s StatefulSet are ordered as expected when 22 // a natsort is done. 23 input := []string{ 24 "memcached-10.memcached.thanos.svc.cluster.local.", 25 "memcached-1.memcached.thanos.svc.cluster.local.", 26 "memcached-6.memcached.thanos.svc.cluster.local.", 27 "memcached-3.memcached.thanos.svc.cluster.local.", 28 "memcached-25.memcached.thanos.svc.cluster.local.", 29 } 30 31 expected := []string{ 32 "memcached-1.memcached.thanos.svc.cluster.local.", 33 "memcached-3.memcached.thanos.svc.cluster.local.", 34 "memcached-6.memcached.thanos.svc.cluster.local.", 35 "memcached-10.memcached.thanos.svc.cluster.local.", 36 "memcached-25.memcached.thanos.svc.cluster.local.", 37 } 38 39 natsort.Sort(input) 40 testutil.Equals(t, expected, input) 41 } 42 43 func TestMemcachedJumpHashSelector_PickServer(t *testing.T) { 44 tests := []struct { 45 addrs []string 46 key string 47 expectedAddr string 48 expectedErr error 49 }{ 50 { 51 addrs: []string{}, 52 key: "test-1", 53 expectedErr: memcache.ErrNoServers, 54 }, 55 { 56 addrs: []string{"127.0.0.1:11211"}, 57 key: "test-1", 58 expectedAddr: "127.0.0.1:11211", 59 }, 60 { 61 addrs: []string{"127.0.0.1:11211", "127.0.0.2:11211"}, 62 key: "test-1", 63 expectedAddr: "127.0.0.1:11211", 64 }, 65 { 66 addrs: []string{"127.0.0.1:11211", "127.0.0.2:11211"}, 67 key: "test-2", 68 expectedAddr: "127.0.0.2:11211", 69 }, 70 } 71 72 s := MemcachedJumpHashSelector{} 73 74 for _, test := range tests { 75 testutil.Ok(t, s.SetServers(test.addrs...)) 76 77 actualAddr, err := s.PickServer(test.key) 78 79 if test.expectedErr != nil { 80 testutil.Equals(t, test.expectedErr, err) 81 testutil.Equals(t, nil, actualAddr) 82 } else { 83 testutil.Ok(t, err) 84 testutil.Equals(t, test.expectedAddr, actualAddr.String()) 85 } 86 } 87 } 88 89 func TestMemcachedJumpHashSelector_Each_ShouldRespectServersOrdering(t *testing.T) { 90 tests := []struct { 91 input []string 92 expected []string 93 }{ 94 { 95 input: []string{"127.0.0.1:11211", "127.0.0.2:11211", "127.0.0.3:11211"}, 96 expected: []string{"127.0.0.1:11211", "127.0.0.2:11211", "127.0.0.3:11211"}, 97 }, 98 { 99 input: []string{"127.0.0.2:11211", "127.0.0.3:11211", "127.0.0.1:11211"}, 100 expected: []string{"127.0.0.1:11211", "127.0.0.2:11211", "127.0.0.3:11211"}, 101 }, 102 } 103 104 s := MemcachedJumpHashSelector{} 105 106 for _, test := range tests { 107 testutil.Ok(t, s.SetServers(test.input...)) 108 109 actual := make([]string, 0, 3) 110 err := s.Each(func(addr net.Addr) error { 111 actual = append(actual, addr.String()) 112 return nil 113 }) 114 115 testutil.Ok(t, err) 116 testutil.Equals(t, test.expected, actual) 117 } 118 } 119 120 func TestMemcachedJumpHashSelector_PickServer_ShouldEvenlyDistributeKeysToServers(t *testing.T) { 121 servers := []string{"127.0.0.1:11211", "127.0.0.2:11211", "127.0.0.3:11211"} 122 selector := MemcachedJumpHashSelector{} 123 testutil.Ok(t, selector.SetServers(servers...)) 124 125 // Calculate the distribution of keys. 126 distribution := make(map[string]int) 127 128 for i := 0; i < 1000; i++ { 129 key := fmt.Sprintf("key-%d", i) 130 addr, err := selector.PickServer(key) 131 testutil.Ok(t, err) 132 distribution[addr.String()]++ 133 } 134 135 // Expect each server got at least 25% of keys, where the perfect split would be 33.3% each. 136 minKeysPerServer := int(float64(len(servers)) * 0.25) 137 testutil.Equals(t, len(servers), len(distribution)) 138 139 for addr, count := range distribution { 140 if count < minKeysPerServer { 141 testutil.Ok(t, errors.Errorf("expected %s to have received at least %d keys instead it received %d", addr, minKeysPerServer, count)) 142 } 143 } 144 } 145 146 func TestMemcachedJumpHashSelector_PickServer_ShouldUseConsistentHashing(t *testing.T) { 147 servers := []string{ 148 "127.0.0.1:11211", 149 "127.0.0.2:11211", 150 "127.0.0.3:11211", 151 "127.0.0.4:11211", 152 "127.0.0.5:11211", 153 "127.0.0.6:11211", 154 "127.0.0.7:11211", 155 "127.0.0.8:11211", 156 "127.0.0.9:11211", 157 } 158 159 selector := MemcachedJumpHashSelector{} 160 testutil.Ok(t, selector.SetServers(servers...)) 161 162 // Pick a server for each key. 163 distribution := make(map[string]string) 164 numKeys := 1000 165 166 for i := 0; i < 1000; i++ { 167 key := fmt.Sprintf("key-%d", i) 168 addr, err := selector.PickServer(key) 169 testutil.Ok(t, err) 170 distribution[key] = addr.String() 171 } 172 173 // Add 1 more server that - in a natural ordering - is added as last. 174 servers = append(servers, "127.0.0.10:11211") 175 testutil.Ok(t, selector.SetServers(servers...)) 176 177 // Calculate the number of keys who has been moved due to the resharding. 178 moved := 0 179 180 for i := 0; i < 1000; i++ { 181 key := fmt.Sprintf("key-%d", i) 182 addr, err := selector.PickServer(key) 183 testutil.Ok(t, err) 184 185 if distribution[key] != addr.String() { 186 moved++ 187 } 188 } 189 190 // Expect we haven't moved more than (1/shards)% +2% tolerance. 191 maxExpectedMovedPerc := (1.0 / float64(len(servers))) + 0.02 192 maxExpectedMoved := int(float64(numKeys) * maxExpectedMovedPerc) 193 if moved > maxExpectedMoved { 194 testutil.Ok(t, errors.Errorf("expected resharding moved no more then %d keys while %d have been moved", maxExpectedMoved, moved)) 195 } 196 } 197 198 func TestMemcachedJumpHashSelector_PickServer_ShouldReturnErrNoServersOnNoServers(t *testing.T) { 199 s := MemcachedJumpHashSelector{} 200 _, err := s.PickServer("foo") 201 testutil.Equals(t, memcache.ErrNoServers, err) 202 } 203 204 func BenchmarkMemcachedJumpHashSelector_PickServer(b *testing.B) { 205 // Create a pretty long list of servers. 206 servers := make([]string, 0) 207 for i := 1; i <= 60; i++ { 208 servers = append(servers, fmt.Sprintf("127.0.0.%d:11211", i)) 209 } 210 211 selector := MemcachedJumpHashSelector{} 212 err := selector.SetServers(servers...) 213 if err != nil { 214 b.Error(err) 215 } 216 217 b.ResetTimer() 218 219 for i := 0; i < b.N; i++ { 220 _, err := selector.PickServer(fmt.Sprint(i)) 221 if err != nil { 222 b.Error(err) 223 } 224 } 225 }