cache_test.go 5.9 KB

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