github.com/shuguocloud/go-zero@v1.3.0/core/stores/cache/cache_test.go (about) 1 package cache 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "math" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/shuguocloud/go-zero/core/errorx" 13 "github.com/shuguocloud/go-zero/core/hash" 14 "github.com/shuguocloud/go-zero/core/stores/redis" 15 "github.com/shuguocloud/go-zero/core/stores/redis/redistest" 16 "github.com/shuguocloud/go-zero/core/syncx" 17 ) 18 19 type mockedNode struct { 20 vals map[string][]byte 21 errNotFound error 22 } 23 24 func (mc *mockedNode) Del(keys ...string) error { 25 var be errorx.BatchError 26 27 for _, key := range keys { 28 if _, ok := mc.vals[key]; !ok { 29 be.Add(mc.errNotFound) 30 } else { 31 delete(mc.vals, key) 32 } 33 } 34 35 return be.Err() 36 } 37 38 func (mc *mockedNode) Get(key string, v interface{}) error { 39 bs, ok := mc.vals[key] 40 if ok { 41 return json.Unmarshal(bs, v) 42 } 43 44 return mc.errNotFound 45 } 46 47 func (mc *mockedNode) IsNotFound(err error) bool { 48 return err == mc.errNotFound 49 } 50 51 func (mc *mockedNode) Set(key string, v interface{}) error { 52 data, err := json.Marshal(v) 53 if err != nil { 54 return err 55 } 56 57 mc.vals[key] = data 58 return nil 59 } 60 61 func (mc *mockedNode) SetWithExpire(key string, v interface{}, expire time.Duration) error { 62 return mc.Set(key, v) 63 } 64 65 func (mc *mockedNode) Take(v interface{}, key string, query func(v interface{}) error) error { 66 if _, ok := mc.vals[key]; ok { 67 return mc.Get(key, v) 68 } 69 70 if err := query(v); err != nil { 71 return err 72 } 73 74 return mc.Set(key, v) 75 } 76 77 func (mc *mockedNode) TakeWithExpire(v interface{}, key string, query func(v interface{}, expire time.Duration) error) error { 78 return mc.Take(v, key, func(v interface{}) error { 79 return query(v, 0) 80 }) 81 } 82 83 func TestCache_SetDel(t *testing.T) { 84 const total = 1000 85 r1, clean1, err := redistest.CreateRedis() 86 assert.Nil(t, err) 87 defer clean1() 88 r2, clean2, err := redistest.CreateRedis() 89 assert.Nil(t, err) 90 defer clean2() 91 conf := ClusterConf{ 92 { 93 RedisConf: redis.RedisConf{ 94 Host: r1.Addr, 95 Type: redis.NodeType, 96 }, 97 Weight: 100, 98 }, 99 { 100 RedisConf: redis.RedisConf{ 101 Host: r2.Addr, 102 Type: redis.NodeType, 103 }, 104 Weight: 100, 105 }, 106 } 107 c := New(conf, syncx.NewSingleFlight(), NewStat("mock"), errPlaceholder) 108 for i := 0; i < total; i++ { 109 if i%2 == 0 { 110 assert.Nil(t, c.Set(fmt.Sprintf("key/%d", i), i)) 111 } else { 112 assert.Nil(t, c.SetWithExpire(fmt.Sprintf("key/%d", i), i, 0)) 113 } 114 } 115 for i := 0; i < total; i++ { 116 var v int 117 assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &v)) 118 assert.Equal(t, i, v) 119 } 120 assert.Nil(t, c.Del()) 121 for i := 0; i < total; i++ { 122 assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i))) 123 } 124 for i := 0; i < total; i++ { 125 var v int 126 assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &v))) 127 assert.Equal(t, 0, v) 128 } 129 } 130 131 func TestCache_OneNode(t *testing.T) { 132 const total = 1000 133 r, clean, err := redistest.CreateRedis() 134 assert.Nil(t, err) 135 defer clean() 136 conf := ClusterConf{ 137 { 138 RedisConf: redis.RedisConf{ 139 Host: r.Addr, 140 Type: redis.NodeType, 141 }, 142 Weight: 100, 143 }, 144 } 145 c := New(conf, syncx.NewSingleFlight(), NewStat("mock"), errPlaceholder) 146 for i := 0; i < total; i++ { 147 if i%2 == 0 { 148 assert.Nil(t, c.Set(fmt.Sprintf("key/%d", i), i)) 149 } else { 150 assert.Nil(t, c.SetWithExpire(fmt.Sprintf("key/%d", i), i, 0)) 151 } 152 } 153 for i := 0; i < total; i++ { 154 var v int 155 assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &v)) 156 assert.Equal(t, i, v) 157 } 158 assert.Nil(t, c.Del()) 159 for i := 0; i < total; i++ { 160 assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i))) 161 } 162 for i := 0; i < total; i++ { 163 var v int 164 assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &v))) 165 assert.Equal(t, 0, v) 166 } 167 } 168 169 func TestCache_Balance(t *testing.T) { 170 const ( 171 numNodes = 100 172 total = 10000 173 ) 174 dispatcher := hash.NewConsistentHash() 175 maps := make([]map[string][]byte, numNodes) 176 for i := 0; i < numNodes; i++ { 177 maps[i] = map[string][]byte{ 178 strconv.Itoa(i): []byte(strconv.Itoa(i)), 179 } 180 } 181 for i := 0; i < numNodes; i++ { 182 dispatcher.AddWithWeight(&mockedNode{ 183 vals: maps[i], 184 errNotFound: errPlaceholder, 185 }, 100) 186 } 187 188 c := cacheCluster{ 189 dispatcher: dispatcher, 190 errNotFound: errPlaceholder, 191 } 192 for i := 0; i < total; i++ { 193 assert.Nil(t, c.Set(strconv.Itoa(i), i)) 194 } 195 196 counts := make(map[int]int) 197 for i, m := range maps { 198 counts[i] = len(m) 199 } 200 entropy := calcEntropy(counts, total) 201 assert.True(t, len(counts) > 1) 202 assert.True(t, entropy > .95, fmt.Sprintf("entropy should be greater than 0.95, but got %.2f", entropy)) 203 204 for i := 0; i < total; i++ { 205 var v int 206 assert.Nil(t, c.Get(strconv.Itoa(i), &v)) 207 assert.Equal(t, i, v) 208 } 209 210 for i := 0; i < total/10; i++ { 211 assert.Nil(t, c.Del(strconv.Itoa(i*10), strconv.Itoa(i*10+1), strconv.Itoa(i*10+2))) 212 assert.Nil(t, c.Del(strconv.Itoa(i*10+9))) 213 } 214 215 var count int 216 for i := 0; i < total/10; i++ { 217 var val int 218 if i%2 == 0 { 219 assert.Nil(t, c.Take(&val, strconv.Itoa(i*10), func(v interface{}) error { 220 *v.(*int) = i 221 count++ 222 return nil 223 })) 224 } else { 225 assert.Nil(t, c.TakeWithExpire(&val, strconv.Itoa(i*10), func(v interface{}, expire time.Duration) error { 226 *v.(*int) = i 227 count++ 228 return nil 229 })) 230 } 231 assert.Equal(t, i, val) 232 } 233 assert.Equal(t, total/10, count) 234 } 235 236 func TestCacheNoNode(t *testing.T) { 237 dispatcher := hash.NewConsistentHash() 238 c := cacheCluster{ 239 dispatcher: dispatcher, 240 errNotFound: errPlaceholder, 241 } 242 assert.NotNil(t, c.Del("foo")) 243 assert.NotNil(t, c.Del("foo", "bar", "any")) 244 assert.NotNil(t, c.Get("foo", nil)) 245 assert.NotNil(t, c.Set("foo", nil)) 246 assert.NotNil(t, c.SetWithExpire("foo", nil, time.Second)) 247 assert.NotNil(t, c.Take(nil, "foo", func(v interface{}) error { 248 return nil 249 })) 250 assert.NotNil(t, c.TakeWithExpire(nil, "foo", func(v interface{}, duration time.Duration) error { 251 return nil 252 })) 253 } 254 255 func calcEntropy(m map[int]int, total int) float64 { 256 var entropy float64 257 258 for _, v := range m { 259 proba := float64(v) / float64(total) 260 entropy -= proba * math.Log2(proba) 261 } 262 263 return entropy / math.Log2(float64(len(m))) 264 }