Refactor cache and disable go-chi cache (#30417)
use built-in cache package to wrap external go-chi cache package
This commit is contained in:
parent
8fd8978b49
commit
c248f010ad
15 changed files with 198 additions and 209 deletions
138
modules/cache/cache.go
vendored
138
modules/cache/cache.go
vendored
|
@ -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()
|
||||
s, err := GetString(key, func() (string, error) {
|
||||
v, err := getFunc()
|
||||
return strconv.FormatInt(v, 10), err
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
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 := 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)
|
||||
}
|
||||
|
|
2
modules/cache/cache_redis.go
vendored
2
modules/cache/cache_redis.go
vendored
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
40
modules/cache/cache_test.go
vendored
40
modules/cache/cache_test.go
vendored
|
@ -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()
|
||||
|
||||
|
|
2
modules/cache/cache_twoqueue.go
vendored
2
modules/cache/cache_twoqueue.go
vendored
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
120
modules/cache/string_cache.go
vendored
Normal file
120
modules/cache/string_cache.go
vendored
Normal file
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue