| package xcache
|
|
|
| import (
|
| "context"
|
| "errors"
|
| "fmt"
|
| "time"
|
|
|
| "github.com/eko/gocache/lib/v4/store"
|
|
|
| cachelib "github.com/eko/gocache/lib/v4/cache"
|
| gocache_store "github.com/eko/gocache/store/go_cache/v4"
|
| gocache "github.com/patrickmn/go-cache"
|
| redis "github.com/redis/go-redis/v9"
|
|
|
| "github.com/looplj/axonhub/internal/log"
|
| redis_store "github.com/looplj/axonhub/internal/pkg/xcache/redis"
|
| "github.com/looplj/axonhub/internal/pkg/xredis"
|
| )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| type Cache[T any] = cachelib.CacheInterface[T]
|
|
|
| type SetterCache[T any] = cachelib.SetterCacheInterface[T]
|
|
|
|
|
|
|
|
|
| func NewMemory[T any](client *gocache.Cache, options ...Option) SetterCache[T] {
|
| store := gocache_store.NewGoCache(client, options...)
|
| return cachelib.New[T](store)
|
| }
|
|
|
|
|
|
|
| func NewMemoryWithOptions[T any](defaultExpiration, cleanupInterval time.Duration, options ...Option) SetterCache[T] {
|
| client := gocache.New(defaultExpiration, cleanupInterval)
|
| return NewMemory[T](client, options...)
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| func NewFromConfig[T any](cfg Config) Cache[T] {
|
|
|
| if cfg.Mode == "" {
|
| return NewNoop[T]()
|
| }
|
|
|
|
|
| memExpiration := defaultIfZero(cfg.Memory.Expiration, 5*time.Minute)
|
| memCleanupInterval := defaultIfZero(cfg.Memory.CleanupInterval, 10*time.Minute)
|
|
|
| memClient := gocache.New(memExpiration, memCleanupInterval)
|
| memStore := gocache_store.NewGoCache(memClient, store.WithExpiration(memExpiration))
|
| mem := cachelib.New[T](memStore)
|
|
|
|
|
| var rds SetterCache[T]
|
|
|
| if (cfg.Redis.Addr != "" || cfg.Redis.URL != "") && cfg.Mode != ModeMemory {
|
| client, err := xredis.NewClient(cfg.Redis)
|
| if err != nil {
|
| panic(fmt.Errorf("invalid redis config: %w", err))
|
| }
|
|
|
| redisExpiration := defaultIfZero(cfg.Redis.Expiration, 30*time.Minute)
|
| rdsStore := redis_store.NewRedisStore[T](client, store.WithExpiration(redisExpiration))
|
| rds = cachelib.New[T](rdsStore)
|
| }
|
|
|
| switch cfg.Mode {
|
| case ModeTwoLevel:
|
| if rds != nil {
|
| log.Info(context.Background(), "Using two-level cache")
|
| return cachelib.NewChain[T](mem, rds)
|
| }
|
|
|
| return mem
|
| case ModeRedis:
|
| if rds == nil {
|
| panic(errors.New("redis cache config is invalid"))
|
| }
|
|
|
| log.Info(context.Background(), "Using redis cache")
|
|
|
| return rds
|
| case ModeMemory:
|
| log.Info(context.Background(), "Using memory cache")
|
| return mem
|
| default:
|
| log.Info(context.Background(), "Disable cache")
|
|
|
| return NewNoop[T]()
|
| }
|
| }
|
|
|
| func defaultIfZero(d, def time.Duration) time.Duration {
|
| if d == 0 {
|
| return def
|
| }
|
|
|
| return d
|
| }
|
|
|
|
|
|
|
| func NewRedis[T any](client *redis.Client, options ...Option) SetterCache[T] {
|
| store := redis_store.NewRedisStore[T](client, options...)
|
| return cachelib.New[T](store)
|
| }
|
|
|
|
|
| func NewRedisWithOptions[T any](opts *redis.Options, options ...Option) SetterCache[T] {
|
| client := redis.NewClient(opts)
|
| return NewRedis[T](client, options...)
|
| }
|
|
|
|
|
|
|
|
|
| func NewTwoLevel[T any](memory SetterCache[T], redis SetterCache[T]) Cache[T] {
|
| return cachelib.NewChain[T](memory, redis)
|
| }
|
|
|
|
|
| func NewTwoLevelWithClients[T any](memClient *gocache.Cache, redisClient *redis.Client, memOptions []Option, redisOptions []Option) Cache[T] {
|
| mem := NewMemory[T](memClient, memOptions...)
|
| rds := NewRedis[T](redisClient, redisOptions...)
|
|
|
| return NewTwoLevel[T](mem, rds)
|
| }
|
|
|