You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache_redis.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cache
  4. import (
  5. "fmt"
  6. "strconv"
  7. "time"
  8. "code.gitea.io/gitea/modules/graceful"
  9. "code.gitea.io/gitea/modules/nosql"
  10. "gitea.com/go-chi/cache"
  11. "github.com/redis/go-redis/v9"
  12. )
  13. // RedisCacher represents a redis cache adapter implementation.
  14. type RedisCacher struct {
  15. c redis.UniversalClient
  16. prefix string
  17. hsetName string
  18. occupyMode bool
  19. }
  20. // toStr convert string/int/int64 interface to string. it's only used by the RedisCacher.Put internally
  21. func toStr(v any) string {
  22. if v == nil {
  23. return ""
  24. }
  25. switch v := v.(type) {
  26. case string:
  27. return v
  28. case []byte:
  29. return string(v)
  30. case int:
  31. return strconv.FormatInt(int64(v), 10)
  32. case int64:
  33. return strconv.FormatInt(v, 10)
  34. default:
  35. return fmt.Sprint(v) // as what the old com.ToStr does in most cases
  36. }
  37. }
  38. // Put puts value (string type) into cache with key and expire time.
  39. // If expired is 0, it lives forever.
  40. func (c *RedisCacher) Put(key string, val any, expire int64) error {
  41. // this function is not well-designed, it only puts string values into cache
  42. key = c.prefix + key
  43. if expire == 0 {
  44. if err := c.c.Set(graceful.GetManager().HammerContext(), key, toStr(val), 0).Err(); err != nil {
  45. return err
  46. }
  47. } else {
  48. dur := time.Duration(expire) * time.Second
  49. if err := c.c.Set(graceful.GetManager().HammerContext(), key, toStr(val), dur).Err(); err != nil {
  50. return err
  51. }
  52. }
  53. if c.occupyMode {
  54. return nil
  55. }
  56. return c.c.HSet(graceful.GetManager().HammerContext(), c.hsetName, key, "0").Err()
  57. }
  58. // Get gets cached value by given key.
  59. func (c *RedisCacher) Get(key string) any {
  60. val, err := c.c.Get(graceful.GetManager().HammerContext(), c.prefix+key).Result()
  61. if err != nil {
  62. return nil
  63. }
  64. return val
  65. }
  66. // Delete deletes cached value by given key.
  67. func (c *RedisCacher) Delete(key string) error {
  68. key = c.prefix + key
  69. if err := c.c.Del(graceful.GetManager().HammerContext(), key).Err(); err != nil {
  70. return err
  71. }
  72. if c.occupyMode {
  73. return nil
  74. }
  75. return c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, key).Err()
  76. }
  77. // Incr increases cached int-type value by given key as a counter.
  78. func (c *RedisCacher) Incr(key string) error {
  79. if !c.IsExist(key) {
  80. return fmt.Errorf("key '%s' not exist", key)
  81. }
  82. return c.c.Incr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
  83. }
  84. // Decr decreases cached int-type value by given key as a counter.
  85. func (c *RedisCacher) Decr(key string) error {
  86. if !c.IsExist(key) {
  87. return fmt.Errorf("key '%s' not exist", key)
  88. }
  89. return c.c.Decr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
  90. }
  91. // IsExist returns true if cached value exists.
  92. func (c *RedisCacher) IsExist(key string) bool {
  93. if c.c.Exists(graceful.GetManager().HammerContext(), c.prefix+key).Val() == 1 {
  94. return true
  95. }
  96. if !c.occupyMode {
  97. c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, c.prefix+key)
  98. }
  99. return false
  100. }
  101. // Flush deletes all cached data.
  102. func (c *RedisCacher) Flush() error {
  103. if c.occupyMode {
  104. return c.c.FlushDB(graceful.GetManager().HammerContext()).Err()
  105. }
  106. keys, err := c.c.HKeys(graceful.GetManager().HammerContext(), c.hsetName).Result()
  107. if err != nil {
  108. return err
  109. }
  110. if err = c.c.Del(graceful.GetManager().HammerContext(), keys...).Err(); err != nil {
  111. return err
  112. }
  113. return c.c.Del(graceful.GetManager().HammerContext(), c.hsetName).Err()
  114. }
  115. // StartAndGC starts GC routine based on config string settings.
  116. // AdapterConfig: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
  117. func (c *RedisCacher) StartAndGC(opts cache.Options) error {
  118. c.hsetName = "MacaronCache"
  119. c.occupyMode = opts.OccupyMode
  120. uri := nosql.ToRedisURI(opts.AdapterConfig)
  121. c.c = nosql.GetManager().GetRedisClient(uri.String())
  122. for k, v := range uri.Query() {
  123. switch k {
  124. case "hset_name":
  125. c.hsetName = v[0]
  126. case "prefix":
  127. c.prefix = v[0]
  128. }
  129. }
  130. return c.c.Ping(graceful.GetManager().HammerContext()).Err()
  131. }
  132. // Ping tests if the cache is alive.
  133. func (c *RedisCacher) Ping() error {
  134. return c.c.Ping(graceful.GetManager().HammerContext()).Err()
  135. }
  136. func init() {
  137. cache.Register("redis", &RedisCacher{})
  138. }