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