mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-03 14:20:43 +00:00
bea6fcaa84
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>
230 lines
6.4 KiB
Go
230 lines
6.4 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cache
|
|
|
|
import (
|
|
"errors"
|
|
"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() {
|
|
defaultCache, _ = NewStringCache(setting.Cache{
|
|
Adapter: "memory",
|
|
TTL: time.Minute,
|
|
})
|
|
setting.CacheService.TTL = 24 * time.Hour
|
|
}
|
|
|
|
func TestNewContext(t *testing.T) {
|
|
assert.NoError(t, Init())
|
|
|
|
setting.CacheService.Cache = setting.Cache{Adapter: "redis", Conn: "some random string"}
|
|
con, err := NewStringCache(setting.Cache{
|
|
Adapter: "rand",
|
|
Conn: "false conf",
|
|
Interval: 100,
|
|
})
|
|
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) {
|
|
defaultCache = nil
|
|
_, err := Test()
|
|
assert.Error(t, err)
|
|
|
|
createTestCache()
|
|
elapsed, err := Test()
|
|
assert.NoError(t, err)
|
|
// mem cache should take from 300ns up to 1ms on modern hardware ...
|
|
assert.Positive(t, elapsed)
|
|
assert.Less(t, elapsed, SlowCacheThreshold)
|
|
}
|
|
|
|
func TestGetCache(t *testing.T) {
|
|
createTestCache()
|
|
|
|
assert.NotNil(t, GetCache())
|
|
}
|
|
|
|
func TestGetString(t *testing.T) {
|
|
createTestCache()
|
|
|
|
data, err := GetString("key", func() (string, error) {
|
|
return "", errors.New("some error")
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Empty(t, data)
|
|
|
|
data, err = GetString("key", func() (string, error) {
|
|
return "", nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, data)
|
|
|
|
data, err = GetString("key", func() (string, error) {
|
|
return "some data", nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, data)
|
|
Remove("key")
|
|
|
|
data, err = GetString("key", func() (string, error) {
|
|
return "some data", nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "some data", data)
|
|
|
|
data, err = GetString("key", func() (string, error) {
|
|
return "", errors.New("some error")
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "some data", data)
|
|
Remove("key")
|
|
}
|
|
|
|
func TestGetInt64(t *testing.T) {
|
|
createTestCache()
|
|
|
|
data, err := GetInt64("key", func() (int64, error) {
|
|
return 0, errors.New("some error")
|
|
})
|
|
assert.Error(t, err)
|
|
assert.EqualValues(t, 0, data)
|
|
|
|
data, err = GetInt64("key", func() (int64, error) {
|
|
return 0, nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.EqualValues(t, 0, data)
|
|
|
|
data, err = GetInt64("key", func() (int64, error) {
|
|
return 100, nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.EqualValues(t, 0, data)
|
|
Remove("key")
|
|
|
|
data, err = GetInt64("key", func() (int64, error) {
|
|
return 100, nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.EqualValues(t, 100, data)
|
|
|
|
data, err = GetInt64("key", func() (int64, error) {
|
|
return 0, errors.New("some error")
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.EqualValues(t, 100, data)
|
|
Remove("key")
|
|
}
|
|
|
|
func TestSafeCacheKey(t *testing.T) {
|
|
assert.Equal(t, "prefix:s-0~", safeCacheKey("prefix", "0~", 100))
|
|
assert.Equal(t, "prefix:h-36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068", safeCacheKey("prefix", " ", 100))
|
|
|
|
assert.Equal(t, "prefix:s-a", safeCacheKey("prefix", "a", 10))
|
|
assert.Equal(t, "prefix:h-961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506", safeCacheKey("prefix", "aa", 10))
|
|
}
|