refactor: move go-chi/cache into Gitea (#39530)

The `gitea.com/go-chi/cache` package only exists for Gitea, so it moves
into `modules/cache`.

- `ITEM_TTL = -1` disables caching as documented
- Sub-second TTLs round up instead of never expiring
- The Redis adapter no longer grows a `MacaronCache` hash
- Use two-queue cache for in-memory cache

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-10-02 18:16:34 +02:00
committed by GitHub
parent 873efed5a6
commit bea6fcaa84
14 changed files with 342 additions and 381 deletions
+95
View File
@@ -8,9 +8,12 @@ import (
"testing"
"time"
"gitea.dev/modules/nosql"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTestCache() {
@@ -32,6 +35,98 @@ func TestNewContext(t *testing.T) {
})
assert.Error(t, err)
assert.Nil(t, con)
_, err = NewStringCache(setting.Cache{Adapter: "memcache"})
assert.ErrorContains(t, err, "requires [cache] HOST")
}
func TestStringCacheAdapters(t *testing.T) {
now := time.Now()
defer test.MockVariableValue(&timeNow, func() time.Time { return now })()
cases := []struct {
adapter string
conns func(t *testing.T) (string, string)
}{
{adapter: "memory", conns: func(*testing.T) (string, string) { return "", "" }},
{adapter: "twoqueue", conns: func(*testing.T) (string, string) { return "100", `{"size":100}` }},
{adapter: "redis", conns: func(t *testing.T) (string, string) {
conn := test.PrepareTestRedis(t) + "?prefix=gitea-test-cache-"
return conn + "first:", conn + "second:"
}},
}
for _, tc := range cases {
t.Run(tc.adapter, func(t *testing.T) {
firstConn, secondConn := tc.conns(t)
first, err := NewStringCache(setting.Cache{Adapter: tc.adapter, Conn: firstConn, Interval: -1})
require.NoError(t, err)
second, err := NewStringCache(setting.Cache{Adapter: tc.adapter, Conn: secondConn, Interval: -1})
require.NoError(t, err)
require.NoError(t, first.Ping())
require.NoError(t, first.Put("key", "value", 0))
value, ok := first.Get("key")
assert.True(t, ok)
assert.Equal(t, "value", value)
assert.True(t, first.IsExist("key"))
assert.False(t, second.IsExist("key"))
require.NoError(t, first.Delete("key"))
_, ok = first.Get("key")
assert.False(t, ok)
assert.False(t, first.IsExist("key"))
require.NoError(t, first.Delete("key"))
require.NoError(t, first.Put("expiring", "value", 10))
if tc.adapter == "redis" {
uri := nosql.ToRedisURI(firstConn)
ttl := nosql.GetManager().GetRedisClient(uri.String()).TTL(t.Context(), uri.Query().Get("prefix")+"expiring").Val()
assert.Positive(t, ttl)
assert.LessOrEqual(t, ttl, 10*time.Second)
require.NoError(t, first.Delete("expiring"))
return
}
now = now.Add(9 * time.Second)
assert.True(t, first.IsExist("expiring"))
now = now.Add(time.Second)
assert.False(t, first.IsExist("expiring"))
_, ok = first.Get("expiring")
assert.False(t, ok)
require.NoError(t, first.Put("expiring", "renewed", 0))
value, ok = first.Get("expiring")
assert.True(t, ok)
assert.Equal(t, "renewed", value)
})
}
}
func TestNegativeItemTTLDisablesOnlyItemTTLCaching(t *testing.T) {
createTestCache()
defer test.MockVariableValue(&setting.CacheService.TTL, -1)()
require.NoError(t, defaultCache.Put("key", "stale", 0))
require.NoError(t, defaultCache.Put("key", "value", setting.CacheService.TTLSeconds()))
assert.False(t, defaultCache.IsExist("key"))
calls := 0
for range 2 {
data, err := GetString("key", func() (string, error) {
calls++
return "value", nil
})
assert.NoError(t, err)
assert.Equal(t, "value", data)
}
assert.Equal(t, 2, calls)
assert.False(t, defaultCache.IsExist("key"))
require.NoError(t, defaultCache.Put("captcha", "value", 600))
assert.True(t, defaultCache.IsExist("captcha"))
}
func TestMemcacheExpiration(t *testing.T) {
defer test.MockVariableValue(&timeNow, func() time.Time { return time.Unix(1000, 0) })()
assert.EqualValues(t, memcacheMaxRelativeTTL, memcacheExpiration(memcacheMaxRelativeTTL))
assert.EqualValues(t, 1000+memcacheMaxRelativeTTL+1, memcacheExpiration(memcacheMaxRelativeTTL+1))
}
func TestTest(t *testing.T) {