diff --git a/cache/cache.go b/cache/cache.go index 2d5c297..6e922c8 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -6,10 +6,8 @@ import ( "strings" "time" - goCache "github.com/patrickmn/go-cache" "github.com/redis/go-redis/v9" "github.com/spf13/viper" - "golang.org/x/exp/maps" "bdo-rest-api/models" "bdo-rest-api/utils" @@ -33,63 +31,6 @@ func joinKeys(keys []string) string { return strings.Join(keys, ",") } -type memoryCache[T any] struct { - internalCache *goCache.Cache - ttl time.Duration -} - -func newMemoryCache[T any]() *memoryCache[T] { - ttl := viper.GetDuration("cachettl") - - return &memoryCache[T]{ - internalCache: goCache.New(ttl, min(time.Hour, ttl)), - ttl: ttl, - } -} - -func (c *memoryCache[T]) AddRecord(keys []string, data T, status int, taskId string) (date, expires string) { - entry := CacheEntry[T]{ - Data: data, - Date: time.Now(), - Status: status, - } - - c.internalCache.Add(joinKeys(keys), entry, c.ttl) - - return utils.FormatDateForHeaders(entry.Date), utils.FormatDateForHeaders(entry.Date.Add(c.ttl)) -} - -func (c *memoryCache[T]) GetRecord(keys []string) (data T, status int, date, expires string, found bool) { - anyEntry, exp, found := c.internalCache.GetWithExpiration(joinKeys(keys)) - - if !found { - return - } - - entry := anyEntry.(CacheEntry[T]) - - return entry.Data, entry.Status, utils.FormatDateForHeaders(entry.Date), utils.FormatDateForHeaders(exp), true -} - -func (c *memoryCache[T]) GetItemCount() int { - return c.internalCache.ItemCount() -} - -func (c *memoryCache[T]) GetKeys() []string { - return maps.Keys(c.internalCache.Items()) -} - -func (c *memoryCache[T]) GetValues() []CacheEntry[T] { - items := c.internalCache.Items() - result := make([]CacheEntry[T], 0, len(items)) - - for _, item := range items { - result = append(result, item.Object.(CacheEntry[T])) - } - - return result -} - type redisCache[T any] struct { client *redis.Client ctx context.Context @@ -182,15 +123,13 @@ var ( ) func InitCache() { - if redisClient, err := newRedisClient(viper.GetString("redis")); err == nil { - GuildProfiles = newRedisCache[models.GuildProfile](redisClient, "gpc") - GuildSearch = newRedisCache[[]models.GuildProfile](redisClient, "gsc") - Profiles = newRedisCache[models.Profile](redisClient, "pc") - ProfileSearch = newRedisCache[[]models.Profile](redisClient, "psc") - } else { - GuildProfiles = newMemoryCache[models.GuildProfile]() - GuildSearch = newMemoryCache[[]models.GuildProfile]() - Profiles = newMemoryCache[models.Profile]() - ProfileSearch = newMemoryCache[[]models.Profile]() + redisClient, err := newRedisClient(viper.GetString("redis")) + if err != nil { + panic(err) } + + GuildProfiles = newRedisCache[models.GuildProfile](redisClient, "gpc") + GuildSearch = newRedisCache[[]models.GuildProfile](redisClient, "gsc") + Profiles = newRedisCache[models.Profile](redisClient, "pc") + ProfileSearch = newRedisCache[[]models.Profile](redisClient, "psc") } diff --git a/cache/cache_test.go b/cache/cache_test.go index 93fb995..ea44a91 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -1,6 +1,7 @@ package cache import ( + "context" "testing" "time" @@ -15,6 +16,19 @@ type testStruct struct { func init() { // Ensure TTL is something predictable viper.Set("cachettl", time.Second*10) + viper.Set("redis", "redis://localhost:6379/0") +} + +func getTestRedisCache[T any](t *testing.T, namespace string) *redisCache[T] { + client, err := newRedisClient(viper.GetString("redis")) + if err != nil { + t.Skip("Skipping test: Redis client could not be created") + } + if err := client.Ping(context.Background()).Err(); err != nil { + t.Skip("Skipping test: Redis server not reachable") + } + client.FlushDB(context.Background()) + return newRedisCache[T](client, namespace) } func TestJoinKeys(t *testing.T) { @@ -26,8 +40,8 @@ func TestJoinKeys(t *testing.T) { } } -func TestMemoryCacheAddAndGetRecord(t *testing.T) { - c := newMemoryCache[testStruct]() +func TestRedisCacheAddAndGetRecord(t *testing.T) { + c := getTestRedisCache[testStruct](t, "test_add_get") data := testStruct{Value: "hello"} keys := []string{"key1", "key2"} @@ -49,8 +63,8 @@ func TestMemoryCacheAddAndGetRecord(t *testing.T) { } } -func TestMemoryCacheMissingRecord(t *testing.T) { - c := newMemoryCache[testStruct]() +func TestRedisCacheMissingRecord(t *testing.T) { + c := getTestRedisCache[testStruct](t, "test_missing") _, _, _, _, found := c.GetRecord([]string{"does", "not", "exist"}) if found { @@ -58,8 +72,8 @@ func TestMemoryCacheMissingRecord(t *testing.T) { } } -func TestMemoryCacheItemCount(t *testing.T) { - c := newMemoryCache[testStruct]() +func TestRedisCacheItemCount(t *testing.T) { + c := getTestRedisCache[testStruct](t, "test_count") if c.GetItemCount() != 0 { t.Fatal("Expected empty cache") @@ -73,8 +87,8 @@ func TestMemoryCacheItemCount(t *testing.T) { } } -func TestMemoryCacheGetKeys(t *testing.T) { - c := newMemoryCache[testStruct]() +func TestRedisCacheGetKeys(t *testing.T) { + c := getTestRedisCache[testStruct](t, "test_keys") c.AddRecord([]string{"k1"}, testStruct{"v1"}, 200, "task1") c.AddRecord([]string{"k2"}, testStruct{"v2"}, 200, "task2") @@ -101,8 +115,8 @@ func TestMemoryCacheGetKeys(t *testing.T) { } } -func TestMemoryCacheGetValues(t *testing.T) { - c := newMemoryCache[testStruct]() +func TestRedisCacheGetValues(t *testing.T) { + c := getTestRedisCache[testStruct](t, "test_values") c.AddRecord([]string{"a"}, testStruct{"aaa"}, 200, "task1") c.AddRecord([]string{"b"}, testStruct{"bbb"}, 200, "task2") diff --git a/go.mod b/go.mod index ec3c14d..4fc917b 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,12 @@ go 1.24.0 require ( github.com/gocolly/colly/v2 v2.3.0 - github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/nzrsky/useragent-generator v1.0.0 github.com/redis/go-redis/v9 v9.17.2 github.com/sa-/slicefunk v0.1.4 github.com/spf13/viper v1.21.0 github.com/ulule/limiter/v3 v3.11.2 go.mongodb.org/mongo-driver/v2 v2.4.1 - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 ) require ( @@ -34,7 +33,6 @@ require ( github.com/kennygrant/sanitize v1.2.4 // indirect github.com/klauspost/compress v1.18.2 // indirect github.com/nlnwa/whatwg-url v0.6.2 // indirect - github.com/nzrsky/useragent-generator v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect diff --git a/go.sum b/go.sum index 76c5205..e3e22f4 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,6 @@ github.com/nlnwa/whatwg-url v0.6.2 h1:jU61lU2ig4LANydbEJmA2nPrtCGiKdtgT0rmMd2VZ/ github.com/nlnwa/whatwg-url v0.6.2/go.mod h1:x0FPXJzzOEieQtsBT/AKvbiBbQ46YlL6Xa7m02M1ECk= github.com/nzrsky/useragent-generator v1.0.0 h1:oEPrFZOmE5HxWXF4rA59asaaBiVtHu8GF0Vbm6tPHE0= github.com/nzrsky/useragent-generator v1.0.0/go.mod h1:KLwn9uNME0wlJP2KmYP5/EvojJFKbFKHBXSgiGeQoUg= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -115,8 +113,6 @@ golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ss golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=