cachenode_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. //go:build !race
  2. // Disable data race detection is because of the timingWheel in cacheNode.
  3. package cache
  4. import (
  5. "errors"
  6. "fmt"
  7. "math/rand"
  8. "runtime"
  9. "strconv"
  10. "sync"
  11. "testing"
  12. "time"
  13. "github.com/alicebob/miniredis/v2"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/zeromicro/go-zero/core/collection"
  16. "github.com/zeromicro/go-zero/core/logx"
  17. "github.com/zeromicro/go-zero/core/mathx"
  18. "github.com/zeromicro/go-zero/core/stat"
  19. "github.com/zeromicro/go-zero/core/stores/redis"
  20. "github.com/zeromicro/go-zero/core/stores/redis/redistest"
  21. "github.com/zeromicro/go-zero/core/syncx"
  22. "github.com/zeromicro/go-zero/core/timex"
  23. )
  24. var errTestNotFound = errors.New("not found")
  25. func init() {
  26. logx.Disable()
  27. stat.SetReporter(nil)
  28. }
  29. func TestCacheNode_DelCache(t *testing.T) {
  30. t.Run("del cache", func(t *testing.T) {
  31. store := redistest.CreateRedis(t)
  32. store.Type = redis.ClusterType
  33. cn := cacheNode{
  34. rds: store,
  35. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  36. lock: new(sync.Mutex),
  37. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  38. stat: NewStat("any"),
  39. errNotFound: errTestNotFound,
  40. }
  41. assert.Nil(t, cn.Del())
  42. assert.Nil(t, cn.Del([]string{}...))
  43. assert.Nil(t, cn.Del(make([]string, 0)...))
  44. cn.Set("first", "one")
  45. assert.Nil(t, cn.Del("first"))
  46. cn.Set("first", "one")
  47. cn.Set("second", "two")
  48. assert.Nil(t, cn.Del("first", "second"))
  49. })
  50. t.Run("del cache with errors", func(t *testing.T) {
  51. old := timingWheel
  52. ticker := timex.NewFakeTicker()
  53. var err error
  54. timingWheel, err = collection.NewTimingWheelWithTicker(
  55. time.Millisecond, timingWheelSlots, func(key, value any) {
  56. clean(key, value)
  57. }, ticker)
  58. assert.NoError(t, err)
  59. t.Cleanup(func() {
  60. timingWheel = old
  61. })
  62. r, err := miniredis.Run()
  63. assert.NoError(t, err)
  64. defer r.Close()
  65. r.SetError("mock error")
  66. node := NewNode(redis.New(r.Addr(), redis.Cluster()), syncx.NewSingleFlight(),
  67. NewStat("any"), errTestNotFound)
  68. assert.NoError(t, node.Del("foo", "bar"))
  69. ticker.Tick()
  70. runtime.Gosched()
  71. })
  72. }
  73. func TestCacheNode_DelCacheWithErrors(t *testing.T) {
  74. store := redistest.CreateRedis(t)
  75. store.Type = redis.ClusterType
  76. cn := cacheNode{
  77. rds: store,
  78. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  79. lock: new(sync.Mutex),
  80. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  81. stat: NewStat("any"),
  82. errNotFound: errTestNotFound,
  83. }
  84. assert.Nil(t, cn.Del("third", "fourth"))
  85. }
  86. func TestCacheNode_InvalidCache(t *testing.T) {
  87. s, err := miniredis.Run()
  88. assert.Nil(t, err)
  89. defer s.Close()
  90. cn := cacheNode{
  91. rds: redis.New(s.Addr()),
  92. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  93. lock: new(sync.Mutex),
  94. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  95. stat: NewStat("any"),
  96. errNotFound: errTestNotFound,
  97. }
  98. s.Set("any", "value")
  99. var str string
  100. assert.NotNil(t, cn.Get("any", &str))
  101. assert.Equal(t, "", str)
  102. _, err = s.Get("any")
  103. assert.Equal(t, miniredis.ErrKeyNotFound, err)
  104. }
  105. func TestCacheNode_SetWithExpire(t *testing.T) {
  106. store := redistest.CreateRedis(t)
  107. cn := cacheNode{
  108. rds: store,
  109. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  110. barrier: syncx.NewSingleFlight(),
  111. lock: new(sync.Mutex),
  112. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  113. stat: NewStat("any"),
  114. errNotFound: errors.New("any"),
  115. }
  116. assert.NotNil(t, cn.SetWithExpire("key", make(chan int), time.Second))
  117. }
  118. func TestCacheNode_Take(t *testing.T) {
  119. store := redistest.CreateRedis(t)
  120. cn := NewNode(store, syncx.NewSingleFlight(), NewStat("any"), errTestNotFound,
  121. WithExpiry(time.Second), WithNotFoundExpiry(time.Second))
  122. var str string
  123. err := cn.Take(&str, "any", func(v any) error {
  124. *v.(*string) = "value"
  125. return nil
  126. })
  127. assert.Nil(t, err)
  128. assert.Equal(t, "value", str)
  129. assert.Nil(t, cn.Get("any", &str))
  130. val, err := store.Get("any")
  131. assert.Nil(t, err)
  132. assert.Equal(t, `"value"`, val)
  133. }
  134. func TestCacheNode_TakeBadRedis(t *testing.T) {
  135. r, err := miniredis.Run()
  136. assert.NoError(t, err)
  137. defer r.Close()
  138. r.SetError("mock error")
  139. cn := NewNode(redis.New(r.Addr()), syncx.NewSingleFlight(), NewStat("any"),
  140. errTestNotFound, WithExpiry(time.Second), WithNotFoundExpiry(time.Second))
  141. var str string
  142. assert.Error(t, cn.Take(&str, "any", func(v any) error {
  143. *v.(*string) = "value"
  144. return nil
  145. }))
  146. }
  147. func TestCacheNode_TakeNotFound(t *testing.T) {
  148. store := redistest.CreateRedis(t)
  149. cn := cacheNode{
  150. rds: store,
  151. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  152. barrier: syncx.NewSingleFlight(),
  153. lock: new(sync.Mutex),
  154. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  155. stat: NewStat("any"),
  156. errNotFound: errTestNotFound,
  157. }
  158. var str string
  159. err := cn.Take(&str, "any", func(v any) error {
  160. return errTestNotFound
  161. })
  162. assert.True(t, cn.IsNotFound(err))
  163. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  164. val, err := store.Get("any")
  165. assert.Nil(t, err)
  166. assert.Equal(t, `*`, val)
  167. store.Set("any", "*")
  168. err = cn.Take(&str, "any", func(v any) error {
  169. return nil
  170. })
  171. assert.True(t, cn.IsNotFound(err))
  172. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  173. store.Del("any")
  174. errDummy := errors.New("dummy")
  175. err = cn.Take(&str, "any", func(v any) error {
  176. return errDummy
  177. })
  178. assert.Equal(t, errDummy, err)
  179. }
  180. func TestCacheNode_TakeNotFoundButChangedByOthers(t *testing.T) {
  181. store := redistest.CreateRedis(t)
  182. cn := cacheNode{
  183. rds: store,
  184. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  185. barrier: syncx.NewSingleFlight(),
  186. lock: new(sync.Mutex),
  187. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  188. stat: NewStat("any"),
  189. errNotFound: errTestNotFound,
  190. }
  191. var str string
  192. err := cn.Take(&str, "any", func(v any) error {
  193. store.Set("any", "foo")
  194. return errTestNotFound
  195. })
  196. assert.True(t, cn.IsNotFound(err))
  197. val, err := store.Get("any")
  198. if assert.NoError(t, err) {
  199. assert.Equal(t, "foo", val)
  200. }
  201. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  202. }
  203. func TestCacheNode_TakeWithExpire(t *testing.T) {
  204. store := redistest.CreateRedis(t)
  205. cn := cacheNode{
  206. rds: store,
  207. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  208. barrier: syncx.NewSingleFlight(),
  209. lock: new(sync.Mutex),
  210. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  211. stat: NewStat("any"),
  212. errNotFound: errors.New("any"),
  213. }
  214. var str string
  215. err := cn.TakeWithExpire(&str, "any", func(v any, expire time.Duration) error {
  216. *v.(*string) = "value"
  217. return nil
  218. })
  219. assert.Nil(t, err)
  220. assert.Equal(t, "value", str)
  221. assert.Nil(t, cn.Get("any", &str))
  222. val, err := store.Get("any")
  223. assert.Nil(t, err)
  224. assert.Equal(t, `"value"`, val)
  225. }
  226. func TestCacheNode_String(t *testing.T) {
  227. store := redistest.CreateRedis(t)
  228. cn := cacheNode{
  229. rds: store,
  230. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  231. barrier: syncx.NewSingleFlight(),
  232. lock: new(sync.Mutex),
  233. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  234. stat: NewStat("any"),
  235. errNotFound: errors.New("any"),
  236. }
  237. assert.Equal(t, store.Addr, cn.String())
  238. }
  239. func TestCacheValueWithBigInt(t *testing.T) {
  240. store := redistest.CreateRedis(t)
  241. cn := cacheNode{
  242. rds: store,
  243. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  244. barrier: syncx.NewSingleFlight(),
  245. lock: new(sync.Mutex),
  246. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  247. stat: NewStat("any"),
  248. errNotFound: errors.New("any"),
  249. }
  250. const (
  251. key = "key"
  252. value int64 = 323427211229009810
  253. )
  254. assert.Nil(t, cn.Set(key, value))
  255. var val any
  256. assert.Nil(t, cn.Get(key, &val))
  257. assert.Equal(t, strconv.FormatInt(value, 10), fmt.Sprintf("%v", val))
  258. }