aboutsummaryrefslogtreecommitdiffstats
path: root/modules/cache
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2024-04-13 16:38:44 +0800
committerGitHub <noreply@github.com>2024-04-13 08:38:44 +0000
commitc248f010ad08a7017ba1d418e9b6a5b72aff0c88 (patch)
tree4e479229bc1248e123f2ed9296edf80fe96cdd65 /modules/cache
parent8fd8978b4934865c2b041216e84e923ad574a4c7 (diff)
downloadgitea-c248f010ad08a7017ba1d418e9b6a5b72aff0c88.tar.gz
gitea-c248f010ad08a7017ba1d418e9b6a5b72aff0c88.zip
Refactor cache and disable go-chi cache (#30417)
use built-in cache package to wrap external go-chi cache package
Diffstat (limited to 'modules/cache')
-rw-r--r--modules/cache/cache.go138
-rw-r--r--modules/cache/cache_redis.go2
-rw-r--r--modules/cache/cache_test.go40
-rw-r--r--modules/cache/cache_twoqueue.go2
-rw-r--r--modules/cache/string_cache.go120
5 files changed, 156 insertions, 146 deletions
diff --git a/modules/cache/cache.go b/modules/cache/cache.go
index 09afc8b7f7..2ca77bdb29 100644
--- a/modules/cache/cache.go
+++ b/modules/cache/cache.go
@@ -4,149 +4,75 @@
package cache
import (
- "fmt"
"strconv"
+ "time"
"code.gitea.io/gitea/modules/setting"
-
- mc "gitea.com/go-chi/cache"
-
- _ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache
)
-var conn mc.Cache
-
-func newCache(cacheConfig setting.Cache) (mc.Cache, error) {
- return mc.NewCacher(mc.Options{
- Adapter: cacheConfig.Adapter,
- AdapterConfig: cacheConfig.Conn,
- Interval: cacheConfig.Interval,
- })
-}
+var defaultCache StringCache
// Init start cache service
func Init() error {
- var err error
-
- if conn == nil {
- if conn, err = newCache(setting.CacheService.Cache); err != nil {
+ if defaultCache == nil {
+ c, err := NewStringCache(setting.CacheService.Cache)
+ if err != nil {
return err
}
- if err = conn.Ping(); err != nil {
+ for i := 0; i < 10; i++ {
+ if err = c.Ping(); err == nil {
+ break
+ }
+ time.Sleep(time.Second)
+ }
+ if err != nil {
return err
}
+ defaultCache = c
}
-
- return err
+ return nil
}
// GetCache returns the currently configured cache
-func GetCache() mc.Cache {
- return conn
+func GetCache() StringCache {
+ return defaultCache
}
// GetString returns the key value from cache with callback when no key exists in cache
func GetString(key string, getFunc func() (string, error)) (string, error) {
- if conn == nil || setting.CacheService.TTL == 0 {
+ if defaultCache == nil || setting.CacheService.TTL == 0 {
return getFunc()
}
-
- cached := conn.Get(key)
-
- if cached == nil {
+ cached, exist := defaultCache.Get(key)
+ if !exist {
value, err := getFunc()
if err != nil {
return value, err
}
- return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
- }
-
- if value, ok := cached.(string); ok {
- return value, nil
- }
-
- if stringer, ok := cached.(fmt.Stringer); ok {
- return stringer.String(), nil
- }
-
- return fmt.Sprintf("%s", cached), nil
-}
-
-// GetInt returns key value from cache with callback when no key exists in cache
-func GetInt(key string, getFunc func() (int, error)) (int, error) {
- if conn == nil || setting.CacheService.TTL == 0 {
- return getFunc()
- }
-
- cached := conn.Get(key)
-
- if cached == nil {
- value, err := getFunc()
- if err != nil {
- return value, err
- }
-
- return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
- }
-
- switch v := cached.(type) {
- case int:
- return v, nil
- case string:
- value, err := strconv.Atoi(v)
- if err != nil {
- return 0, err
- }
- return value, nil
- default:
- value, err := getFunc()
- if err != nil {
- return value, err
- }
- return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
+ return value, defaultCache.Put(key, value, setting.CacheService.TTLSeconds())
}
+ return cached, nil
}
// GetInt64 returns key value from cache with callback when no key exists in cache
func GetInt64(key string, getFunc func() (int64, error)) (int64, error) {
- if conn == nil || setting.CacheService.TTL == 0 {
- return getFunc()
- }
-
- cached := conn.Get(key)
-
- if cached == nil {
- value, err := getFunc()
- if err != nil {
- return value, err
- }
-
- return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
+ s, err := GetString(key, func() (string, error) {
+ v, err := getFunc()
+ return strconv.FormatInt(v, 10), err
+ })
+ if err != nil {
+ return 0, err
}
-
- switch v := conn.Get(key).(type) {
- case int64:
- return v, nil
- case string:
- value, err := strconv.ParseInt(v, 10, 64)
- if err != nil {
- return 0, err
- }
- return value, nil
- default:
- value, err := getFunc()
- if err != nil {
- return value, err
- }
-
- return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
+ if s == "" {
+ return 0, nil
}
+ return strconv.ParseInt(s, 10, 64)
}
// Remove key from cache
func Remove(key string) {
- if conn == nil {
+ if defaultCache == nil {
return
}
- _ = conn.Delete(key)
+ _ = defaultCache.Delete(key)
}
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
index 6c358b0a78..c5b52a2086 100644
--- a/modules/cache/cache_redis.go
+++ b/modules/cache/cache_redis.go
@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/nosql"
- "gitea.com/go-chi/cache"
+ "gitea.com/go-chi/cache" //nolint:depguard
"github.com/redis/go-redis/v9"
)
diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go
index 3f65040924..0c68cc26ee 100644
--- a/modules/cache/cache_test.go
+++ b/modules/cache/cache_test.go
@@ -14,7 +14,7 @@ import (
)
func createTestCache() {
- conn, _ = newCache(setting.Cache{
+ defaultCache, _ = NewStringCache(setting.Cache{
Adapter: "memory",
TTL: time.Minute,
})
@@ -25,7 +25,7 @@ func TestNewContext(t *testing.T) {
assert.NoError(t, Init())
setting.CacheService.Cache = setting.Cache{Adapter: "redis", Conn: "some random string"}
- con, err := newCache(setting.Cache{
+ con, err := NewStringCache(setting.Cache{
Adapter: "rand",
Conn: "false conf",
Interval: 100,
@@ -76,42 +76,6 @@ func TestGetString(t *testing.T) {
Remove("key")
}
-func TestGetInt(t *testing.T) {
- createTestCache()
-
- data, err := GetInt("key", func() (int, error) {
- return 0, fmt.Errorf("some error")
- })
- assert.Error(t, err)
- assert.Equal(t, 0, data)
-
- data, err = GetInt("key", func() (int, error) {
- return 0, nil
- })
- assert.NoError(t, err)
- assert.Equal(t, 0, data)
-
- data, err = GetInt("key", func() (int, error) {
- return 100, nil
- })
- assert.NoError(t, err)
- assert.Equal(t, 0, data)
- Remove("key")
-
- data, err = GetInt("key", func() (int, error) {
- return 100, nil
- })
- assert.NoError(t, err)
- assert.Equal(t, 100, data)
-
- data, err = GetInt("key", func() (int, error) {
- return 0, fmt.Errorf("some error")
- })
- assert.NoError(t, err)
- assert.Equal(t, 100, data)
- Remove("key")
-}
-
func TestGetInt64(t *testing.T) {
createTestCache()
diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go
index f9de2563ec..1eda2debc4 100644
--- a/modules/cache/cache_twoqueue.go
+++ b/modules/cache/cache_twoqueue.go
@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/modules/json"
- mc "gitea.com/go-chi/cache"
+ mc "gitea.com/go-chi/cache" //nolint:depguard
lru "github.com/hashicorp/golang-lru/v2"
)
diff --git a/modules/cache/string_cache.go b/modules/cache/string_cache.go
new file mode 100644
index 0000000000..4f659616f5
--- /dev/null
+++ b/modules/cache/string_cache.go
@@ -0,0 +1,120 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+ "errors"
+ "strings"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+
+ chi_cache "gitea.com/go-chi/cache" //nolint:depguard
+)
+
+type GetJSONError struct {
+ err error
+ cachedError string // Golang error can't be stored in cache, only the string message could be stored
+}
+
+func (e *GetJSONError) ToError() error {
+ if e.err != nil {
+ return e.err
+ }
+ return errors.New("cached error: " + e.cachedError)
+}
+
+type StringCache interface {
+ Ping() error
+
+ Get(key string) (string, bool)
+ Put(key, value string, ttl int64) error
+ Delete(key string) error
+ IsExist(key string) bool
+
+ PutJSON(key string, v any, ttl int64) error
+ GetJSON(key string, ptr any) (exist bool, err *GetJSONError)
+
+ ChiCache() chi_cache.Cache
+}
+
+type stringCache struct {
+ chiCache chi_cache.Cache
+}
+
+func NewStringCache(cacheConfig setting.Cache) (StringCache, error) {
+ adapter := util.IfZero(cacheConfig.Adapter, "memory")
+ interval := util.IfZero(cacheConfig.Interval, 60)
+ cc, err := chi_cache.NewCacher(chi_cache.Options{
+ Adapter: adapter,
+ AdapterConfig: cacheConfig.Conn,
+ Interval: interval,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return &stringCache{chiCache: cc}, nil
+}
+
+func (sc *stringCache) Ping() error {
+ return sc.chiCache.Ping()
+}
+
+func (sc *stringCache) Get(key string) (string, bool) {
+ v := sc.chiCache.Get(key)
+ if v == nil {
+ return "", false
+ }
+ s, ok := v.(string)
+ return s, ok
+}
+
+func (sc *stringCache) Put(key, value string, ttl int64) error {
+ return sc.chiCache.Put(key, value, ttl)
+}
+
+func (sc *stringCache) Delete(key string) error {
+ return sc.chiCache.Delete(key)
+}
+
+func (sc *stringCache) IsExist(key string) bool {
+ return sc.chiCache.IsExist(key)
+}
+
+const cachedErrorPrefix = "<CACHED-ERROR>:"
+
+func (sc *stringCache) PutJSON(key string, v any, ttl int64) error {
+ var s string
+ switch v := v.(type) {
+ case error:
+ s = cachedErrorPrefix + v.Error()
+ default:
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+ s = util.UnsafeBytesToString(b)
+ }
+ return sc.chiCache.Put(key, s, ttl)
+}
+
+func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) {
+ s, ok := sc.Get(key)
+ if !ok || s == "" {
+ return false, nil
+ }
+ s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix)
+ if isCachedError {
+ return true, &GetJSONError{cachedError: s}
+ }
+ if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil {
+ return false, &GetJSONError{err: err}
+ }
+ return true, nil
+}
+
+func (sc *stringCache) ChiCache() chi_cache.Cache {
+ return sc.chiCache
+}