From 06adbc3e4941abd6370db0b483e367265aa9cf0e Mon Sep 17 00:00:00 2001 From: bryanqiu Date: Thu, 6 Apr 2023 18:41:54 +0800 Subject: [PATCH] v1.1.0 --- cache.go | 90 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/cache.go b/cache.go index 01c2194..c159a96 100644 --- a/cache.go +++ b/cache.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gomodule/redigo/redis" + "qoobing.com/gomod/log" ) var ( @@ -50,8 +51,11 @@ func NewCache[T any](getter Getter[T], cfg Config) Cacher[T] { if cfg.UseRedisCache && cfg.RedisCacheConn != nil { c.redisCache = new(redisCacher[T]) c.redisCache.rds = cfg.RedisCacheConn - c.redisCache.cachePrefix = cfg.RedisCacheKeyPrefix c.redisCache.cacheSecond = time.Duration(cfg.RedisCacheLifetimeSecond) * time.Second + if cfg.RedisCacheKeyPrefix == "" { + panic("redis cache's key prefix must not be null") + } + c.redisCache.cachePrefix = fmt.Sprintf("%s-cache-id:", cfg.RedisCacheKeyPrefix) } else if cfg.UseRedisCache { panic("want to user redis cache, but redis.Conn is nil") } @@ -60,8 +64,8 @@ func NewCache[T any](getter Getter[T], cfg Config) Cacher[T] { } type cacheItem[T any] struct { - Data T //cache data - CreateTime time.Time //cache create time + Data *T `json:"d"` //cache data + CreateTime time.Time `json:"t"` //cache create time } type localCacher[T any] struct { @@ -69,7 +73,7 @@ type localCacher[T any] struct { cacheSecond time.Duration } -type redisCacher struct { +type redisCacher[T any] struct { rds redis.Conn cachePrefix string cacheSecond time.Duration @@ -81,42 +85,46 @@ type cacher[T any] struct { redisCache *redisCacher[T] } -func (c *localCacher) GetFromCache(id string) (t *T, err error) { +func (c *localCacher[T]) GetFromCache(id string) (t *T, err error) { var earliestCreateTime = time.Now().Add(-c.cacheSecond) - if a, ok := c.cacheItems[appid]; !ok { - return nil, ErrAppNotExist - } else if a.CreateTime.Before(earliestCreateTime) { - return a, nil - } else if ok { - delete(c.apps, appid) - return nil, ErrAppNotExist + if a, ok := c.cacheItems[id]; !ok { + return nil, ErrNotFound + } else if earliestCreateTime.Before(a.CreateTime) { + return a.Data, nil } + //TODO: cocurrent + delete(c.cacheItems, id) + return nil, ErrNotFound } -func (c *localCacher) SetIntoCache(id string, t *T) (err error) { - c.apps[id] = t +func (c *localCacher[T]) SetIntoCache(id string, t *T) (err error) { + c.cacheItems[id] = &cacheItem[T]{ + Data: t, + CreateTime: time.Now(), + } + fmt.Println("cacheItems", c.cacheItems) return nil } -func (c *redisCacher) GetFromCache(id string) (*T, error) { +func (c *redisCacher[T]) GetFromCache(id string) (*T, error) { var ( rds = c.rds - redisKey = fmt.Sprintf("appconfig-by-appid:%s", appid) + redisKey = c.cachePrefix + id redisValue = "" err error = nil ) // Step 1. read from redis if redisValue, err = redis.String(rds.Do("GET", redisKey)); err != nil { - log.Infof("get appconfig cache failed: 'redis return<%s>'", err.Error()) - return nil, ErrAppNotExist + log.Infof("get cache failed: 'redis return<%s>'", err.Error()) + return nil, ErrNotFound } // Step 2. decode from string - a := &App{} + a := &cacheItem[T]{} if err = json.Unmarshal([]byte(redisValue), a); err != nil { - log.Errorf("get appconfig cache failed: 'json unmarshal failed <%s>'", err.Error()) - return nil, ErrAppNotExist + log.Errorf("get cache failed: 'json unmarshal failed <%s>'", err.Error()) + return nil, ErrNotFound } // Step 3. check expire time @@ -124,32 +132,33 @@ func (c *redisCacher) GetFromCache(id string) (*T, error) { if d.Nanoseconds() > 0 { var earliestCreateTime = time.Now().Add(-c.cacheSecond) if a.CreateTime.Before(earliestCreateTime) { - log.Infof("app(%s) is in redis cache but expired", appid) - return nil, ErrAppNotExist + log.Infof("app(%s) is in redis cache but expired", id) + return nil, ErrNotFound } } - return a, nil + return a.Data, nil } -func (c *redisCacher) SetIntoCache(appid string, a *App) error { +func (c *redisCacher[T]) SetIntoCache(id string, t *T) error { var ( rds = c.rds - redisKey = fmt.Sprintf("appconfig-by-appid:%s", appid) + redisKey = c.cachePrefix + id redisValue = []byte{} err error = nil ) // Step 1. decode from string + var a = cacheItem[T]{Data: t, CreateTime: time.Now()} if redisValue, err = json.Marshal(a); err != nil { - log.Errorf("set appconfig cache failed: 'json marshal failed <%s>'", err.Error()) - return errors.New("set appconfig cache failed: 'marshal failed'") + log.Errorf("set cache failed: 'json marshal failed <%s>'", err.Error()) + return errors.New("set cache failed: 'marshal failed'") } // Step 2. read from redis if _, err = rds.Do("SET", redisKey, string(redisValue)); err != nil { - log.Errorf("set appconfig cache failed: 'redis failed <%s>'", err.Error()) - return errors.New("set appconfig cache failed: 'redis failed'") + log.Errorf("set cache failed: 'redis failed <%s>'", err.Error()) + return errors.New("set cache failed: 'redis failed'") } return nil @@ -174,27 +183,30 @@ func (c *cacher[T]) GetFromCache(id string) (dat *T, err error) { // Step 3. get from [redis] if c.redisCache != nil { if dat, err := c.redisCache.GetFromCache(id); err == nil { + if c.localCache != nil { + log.Infof("set cache(id:%s) to localCache by redisCacher") + c.localCache.SetIntoCache(id, dat) + } log.Infof("get cache(id:%s) from redisCacher success", id) return dat, nil } else if c.getter == nil { - log.Warningf("get cache(id:%s) from local failed, and storager is nil, "+ + log.Warningf("get cache(id:%s) from all cache failed, and storager is nil, "+ "trade as not found. may be you forgot set in db ???", id) - return nil, ErrAppNotExist + return nil, ErrNotFound } } if c.getter == nil { - log.Warninf("get cache(id:%s) from all cache failed, and storager is nil, "+ + log.Warningf("get cache(id:%s) from all cache failed, and storager is nil, "+ "trade as not found. may be you forgot set in db ???", id) - return nil, ErrAppNotExist + return nil, ErrNotFound } // Step 4. get from [storager(database or somewhere)] - dat, err := c.getter.GetAppId(id) + dat, err = c.getter.GetById(id) if err != nil { log.Errorf("cache(id:%s) is not in database, something error: %s", id, err) - return nil, ErrAppNotExist + return nil, ErrNotFound } - dat.CreateTime = time.Now() log.Infof("get cache(id:%s) from exportStorager(maybe database) success", id) // Step 5. set to cacha @@ -215,6 +227,8 @@ func (c *cacher[T]) SetIntoCache(id string, dat *T) (err error) { if c.localCache != nil { if err = c.localCache.SetIntoCache(id, dat); err != nil { log.Infof("set data into localCache failed, err:%s", err) + } else { + log.Debugf("success set data into localCache, id: %s", id) } } @@ -222,6 +236,8 @@ func (c *cacher[T]) SetIntoCache(id string, dat *T) (err error) { if c.redisCache != nil { if err = c.redisCache.SetIntoCache(id, dat); err != nil { log.Infof("set data into redisCache failed, err:%s", err) + } else { + log.Debugf("success set data into redisCache, id: %s", id) } } return nil