cachenode_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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. r, err := miniredis.Run()
  32. assert.NoError(t, err)
  33. defer r.Close()
  34. store := redis.New(r.Addr(), redis.Cluster())
  35. cn := cacheNode{
  36. rds: store,
  37. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  38. lock: new(sync.Mutex),
  39. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  40. stat: NewStat("any"),
  41. errNotFound: errTestNotFound,
  42. }
  43. assert.Nil(t, cn.Del())
  44. assert.Nil(t, cn.Del([]string{}...))
  45. assert.Nil(t, cn.Del(make([]string, 0)...))
  46. cn.Set("first", "one")
  47. assert.Nil(t, cn.Del("first"))
  48. cn.Set("first", "one")
  49. cn.Set("second", "two")
  50. assert.Nil(t, cn.Del("first", "second"))
  51. })
  52. t.Run("del cache with errors", func(t *testing.T) {
  53. old := timingWheel
  54. ticker := timex.NewFakeTicker()
  55. var err error
  56. timingWheel, err = collection.NewTimingWheelWithTicker(
  57. time.Millisecond, timingWheelSlots, func(key, value any) {
  58. clean(key, value)
  59. }, ticker)
  60. assert.NoError(t, err)
  61. t.Cleanup(func() {
  62. timingWheel = old
  63. })
  64. r, err := miniredis.Run()
  65. assert.NoError(t, err)
  66. defer r.Close()
  67. r.SetError("mock error")
  68. node := NewNode(redis.New(r.Addr(), redis.Cluster()), syncx.NewSingleFlight(),
  69. NewStat("any"), errTestNotFound)
  70. assert.NoError(t, node.Del("foo", "bar"))
  71. ticker.Tick()
  72. runtime.Gosched()
  73. })
  74. }
  75. func TestCacheNode_DelCacheWithErrors(t *testing.T) {
  76. store := redistest.CreateRedis(t)
  77. store.Type = redis.ClusterType
  78. cn := cacheNode{
  79. rds: store,
  80. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  81. lock: new(sync.Mutex),
  82. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  83. stat: NewStat("any"),
  84. errNotFound: errTestNotFound,
  85. }
  86. assert.Nil(t, cn.Del("third", "fourth"))
  87. }
  88. func TestCacheNode_InvalidCache(t *testing.T) {
  89. s, err := miniredis.Run()
  90. assert.Nil(t, err)
  91. defer s.Close()
  92. cn := cacheNode{
  93. rds: redis.New(s.Addr()),
  94. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  95. lock: new(sync.Mutex),
  96. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  97. stat: NewStat("any"),
  98. errNotFound: errTestNotFound,
  99. }
  100. s.Set("any", "value")
  101. var str string
  102. assert.NotNil(t, cn.Get("any", &str))
  103. assert.Equal(t, "", str)
  104. _, err = s.Get("any")
  105. assert.Equal(t, miniredis.ErrKeyNotFound, err)
  106. }
  107. func TestCacheNode_SetWithExpire(t *testing.T) {
  108. store := redistest.CreateRedis(t)
  109. cn := cacheNode{
  110. rds: store,
  111. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  112. barrier: syncx.NewSingleFlight(),
  113. lock: new(sync.Mutex),
  114. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  115. stat: NewStat("any"),
  116. errNotFound: errors.New("any"),
  117. }
  118. assert.NotNil(t, cn.SetWithExpire("key", make(chan int), time.Second))
  119. }
  120. func TestCacheNode_Take(t *testing.T) {
  121. store := redistest.CreateRedis(t)
  122. cn := NewNode(store, syncx.NewSingleFlight(), NewStat("any"), errTestNotFound,
  123. WithExpiry(time.Second), WithNotFoundExpiry(time.Second))
  124. var str string
  125. err := cn.Take(&str, "any", func(v any) error {
  126. *v.(*string) = "value"
  127. return nil
  128. })
  129. assert.Nil(t, err)
  130. assert.Equal(t, "value", str)
  131. assert.Nil(t, cn.Get("any", &str))
  132. val, err := store.Get("any")
  133. assert.Nil(t, err)
  134. assert.Equal(t, `"value"`, val)
  135. }
  136. func TestCacheNode_TakeBadRedis(t *testing.T) {
  137. r, err := miniredis.Run()
  138. assert.NoError(t, err)
  139. defer r.Close()
  140. r.SetError("mock error")
  141. cn := NewNode(redis.New(r.Addr()), syncx.NewSingleFlight(), NewStat("any"),
  142. errTestNotFound, WithExpiry(time.Second), WithNotFoundExpiry(time.Second))
  143. var str string
  144. assert.Error(t, cn.Take(&str, "any", func(v any) error {
  145. *v.(*string) = "value"
  146. return nil
  147. }))
  148. }
  149. func TestCacheNode_TakeNotFound(t *testing.T) {
  150. t.Run("not found", func(t *testing.T) {
  151. store := redistest.CreateRedis(t)
  152. cn := cacheNode{
  153. rds: store,
  154. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  155. barrier: syncx.NewSingleFlight(),
  156. lock: new(sync.Mutex),
  157. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  158. stat: NewStat("any"),
  159. errNotFound: errTestNotFound,
  160. }
  161. var str string
  162. err := cn.Take(&str, "any", func(v any) error {
  163. return errTestNotFound
  164. })
  165. assert.True(t, cn.IsNotFound(err))
  166. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  167. val, err := store.Get("any")
  168. assert.Nil(t, err)
  169. assert.Equal(t, `*`, val)
  170. store.Set("any", "*")
  171. err = cn.Take(&str, "any", func(v any) error {
  172. return nil
  173. })
  174. assert.True(t, cn.IsNotFound(err))
  175. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  176. store.Del("any")
  177. errDummy := errors.New("dummy")
  178. err = cn.Take(&str, "any", func(v any) error {
  179. return errDummy
  180. })
  181. assert.Equal(t, errDummy, err)
  182. })
  183. t.Run("not found with redis error", func(t *testing.T) {
  184. r, err := miniredis.Run()
  185. assert.NoError(t, err)
  186. defer r.Close()
  187. store, err := redis.NewRedis(redis.RedisConf{
  188. Host: r.Addr(),
  189. Type: redis.NodeType,
  190. })
  191. assert.NoError(t, err)
  192. cn := cacheNode{
  193. rds: store,
  194. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  195. barrier: syncx.NewSingleFlight(),
  196. lock: new(sync.Mutex),
  197. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  198. stat: NewStat("any"),
  199. errNotFound: errTestNotFound,
  200. }
  201. var str string
  202. err = cn.Take(&str, "any", func(v any) error {
  203. r.SetError("mock error")
  204. return errTestNotFound
  205. })
  206. assert.True(t, cn.IsNotFound(err))
  207. })
  208. }
  209. func TestCacheNode_TakeCtxWithRedisError(t *testing.T) {
  210. t.Run("not found with redis error", func(t *testing.T) {
  211. r, err := miniredis.Run()
  212. assert.NoError(t, err)
  213. defer r.Close()
  214. store, err := redis.NewRedis(redis.RedisConf{
  215. Host: r.Addr(),
  216. Type: redis.NodeType,
  217. })
  218. assert.NoError(t, err)
  219. cn := cacheNode{
  220. rds: store,
  221. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  222. barrier: syncx.NewSingleFlight(),
  223. lock: new(sync.Mutex),
  224. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  225. stat: NewStat("any"),
  226. errNotFound: errTestNotFound,
  227. }
  228. var str string
  229. err = cn.Take(&str, "any", func(v any) error {
  230. str = "foo"
  231. r.SetError("mock error")
  232. return nil
  233. })
  234. assert.NoError(t, err)
  235. })
  236. }
  237. func TestCacheNode_TakeNotFoundButChangedByOthers(t *testing.T) {
  238. store := redistest.CreateRedis(t)
  239. cn := cacheNode{
  240. rds: store,
  241. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  242. barrier: syncx.NewSingleFlight(),
  243. lock: new(sync.Mutex),
  244. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  245. stat: NewStat("any"),
  246. errNotFound: errTestNotFound,
  247. }
  248. var str string
  249. err := cn.Take(&str, "any", func(v any) error {
  250. store.Set("any", "foo")
  251. return errTestNotFound
  252. })
  253. assert.True(t, cn.IsNotFound(err))
  254. val, err := store.Get("any")
  255. if assert.NoError(t, err) {
  256. assert.Equal(t, "foo", val)
  257. }
  258. assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
  259. }
  260. func TestCacheNode_TakeWithExpire(t *testing.T) {
  261. store := redistest.CreateRedis(t)
  262. cn := cacheNode{
  263. rds: store,
  264. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  265. barrier: syncx.NewSingleFlight(),
  266. lock: new(sync.Mutex),
  267. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  268. stat: NewStat("any"),
  269. errNotFound: errors.New("any"),
  270. }
  271. var str string
  272. err := cn.TakeWithExpire(&str, "any", func(v any, expire time.Duration) error {
  273. *v.(*string) = "value"
  274. return nil
  275. })
  276. assert.Nil(t, err)
  277. assert.Equal(t, "value", str)
  278. assert.Nil(t, cn.Get("any", &str))
  279. val, err := store.Get("any")
  280. assert.Nil(t, err)
  281. assert.Equal(t, `"value"`, val)
  282. }
  283. func TestCacheNode_String(t *testing.T) {
  284. store := redistest.CreateRedis(t)
  285. cn := cacheNode{
  286. rds: store,
  287. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  288. barrier: syncx.NewSingleFlight(),
  289. lock: new(sync.Mutex),
  290. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  291. stat: NewStat("any"),
  292. errNotFound: errors.New("any"),
  293. }
  294. assert.Equal(t, store.Addr, cn.String())
  295. }
  296. func TestCacheValueWithBigInt(t *testing.T) {
  297. store := redistest.CreateRedis(t)
  298. cn := cacheNode{
  299. rds: store,
  300. r: rand.New(rand.NewSource(time.Now().UnixNano())),
  301. barrier: syncx.NewSingleFlight(),
  302. lock: new(sync.Mutex),
  303. unstableExpiry: mathx.NewUnstable(expiryDeviation),
  304. stat: NewStat("any"),
  305. errNotFound: errors.New("any"),
  306. }
  307. const (
  308. key = "key"
  309. value int64 = 323427211229009810
  310. )
  311. assert.Nil(t, cn.Set(key, value))
  312. var val any
  313. assert.Nil(t, cn.Get(key, &val))
  314. assert.Equal(t, strconv.FormatInt(value, 10), fmt.Sprintf("%v", val))
  315. }