summaryrefslogtreecommitdiffstats
path: root/modules/cache/cache_redis.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-12-12 23:57:56 +0100
commite68b9d00a6e05b3a941f63ffb696f91e554ac5ec (patch)
tree97775d6c13b0f416af55314eb6a89ef792474615 /modules/cache/cache_redis.go
parentInitial commit. (diff)
downloadforgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.tar.xz
forgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.zip
Adding upstream version 9.0.3.
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'modules/cache/cache_redis.go')
-rw-r--r--modules/cache/cache_redis.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
new file mode 100644
index 0000000..4c243b2
--- /dev/null
+++ b/modules/cache/cache_redis.go
@@ -0,0 +1,161 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+ "fmt"
+ "strconv"
+ "time"
+
+ "code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/nosql"
+
+ "code.forgejo.org/go-chi/cache"
+)
+
+// RedisCacher represents a redis cache adapter implementation.
+type RedisCacher struct {
+ c nosql.RedisClient
+ prefix string
+ hsetName string
+ occupyMode bool
+}
+
+// toStr convert string/int/int64 interface to string. it's only used by the RedisCacher.Put internally
+func toStr(v any) string {
+ if v == nil {
+ return ""
+ }
+ switch v := v.(type) {
+ case string:
+ return v
+ case []byte:
+ return string(v)
+ case int:
+ return strconv.FormatInt(int64(v), 10)
+ case int64:
+ return strconv.FormatInt(v, 10)
+ default:
+ return fmt.Sprint(v) // as what the old com.ToStr does in most cases
+ }
+}
+
+// Put puts value (string type) into cache with key and expire time.
+// If expired is 0, it lives forever.
+func (c *RedisCacher) Put(key string, val any, expire int64) error {
+ // this function is not well-designed, it only puts string values into cache
+ key = c.prefix + key
+ if expire == 0 {
+ if err := c.c.Set(graceful.GetManager().HammerContext(), key, toStr(val), 0).Err(); err != nil {
+ return err
+ }
+ } else {
+ dur := time.Duration(expire) * time.Second
+ if err := c.c.Set(graceful.GetManager().HammerContext(), key, toStr(val), dur).Err(); err != nil {
+ return err
+ }
+ }
+
+ if c.occupyMode {
+ return nil
+ }
+ return c.c.HSet(graceful.GetManager().HammerContext(), c.hsetName, key, "0").Err()
+}
+
+// Get gets cached value by given key.
+func (c *RedisCacher) Get(key string) any {
+ val, err := c.c.Get(graceful.GetManager().HammerContext(), c.prefix+key).Result()
+ if err != nil {
+ return nil
+ }
+ return val
+}
+
+// Delete deletes cached value by given key.
+func (c *RedisCacher) Delete(key string) error {
+ key = c.prefix + key
+ if err := c.c.Del(graceful.GetManager().HammerContext(), key).Err(); err != nil {
+ return err
+ }
+
+ if c.occupyMode {
+ return nil
+ }
+ return c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, key).Err()
+}
+
+// Incr increases cached int-type value by given key as a counter.
+func (c *RedisCacher) Incr(key string) error {
+ if !c.IsExist(key) {
+ return fmt.Errorf("key '%s' not exist", key)
+ }
+ return c.c.Incr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
+}
+
+// Decr decreases cached int-type value by given key as a counter.
+func (c *RedisCacher) Decr(key string) error {
+ if !c.IsExist(key) {
+ return fmt.Errorf("key '%s' not exist", key)
+ }
+ return c.c.Decr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
+}
+
+// IsExist returns true if cached value exists.
+func (c *RedisCacher) IsExist(key string) bool {
+ if c.c.Exists(graceful.GetManager().HammerContext(), c.prefix+key).Val() == 1 {
+ return true
+ }
+
+ if !c.occupyMode {
+ c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, c.prefix+key)
+ }
+ return false
+}
+
+// Flush deletes all cached data.
+func (c *RedisCacher) Flush() error {
+ if c.occupyMode {
+ return c.c.FlushDB(graceful.GetManager().HammerContext()).Err()
+ }
+
+ keys, err := c.c.HKeys(graceful.GetManager().HammerContext(), c.hsetName).Result()
+ if err != nil {
+ return err
+ }
+ if err = c.c.Del(graceful.GetManager().HammerContext(), keys...).Err(); err != nil {
+ return err
+ }
+ return c.c.Del(graceful.GetManager().HammerContext(), c.hsetName).Err()
+}
+
+// StartAndGC starts GC routine based on config string settings.
+// AdapterConfig: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
+func (c *RedisCacher) StartAndGC(opts cache.Options) error {
+ c.hsetName = "MacaronCache"
+ c.occupyMode = opts.OccupyMode
+
+ uri := nosql.ToRedisURI(opts.AdapterConfig)
+
+ c.c = nosql.GetManager().GetRedisClient(uri.String())
+
+ for k, v := range uri.Query() {
+ switch k {
+ case "hset_name":
+ c.hsetName = v[0]
+ case "prefix":
+ c.prefix = v[0]
+ }
+ }
+
+ return c.c.Ping(graceful.GetManager().HammerContext()).Err()
+}
+
+// Ping tests if the cache is alive.
+func (c *RedisCacher) Ping() error {
+ return c.c.Ping(graceful.GetManager().HammerContext()).Err()
+}
+
+func init() {
+ cache.Register("redis", &RedisCacher{})
+}