This commit is contained in:
bryanqiu 2023-04-06 18:41:54 +08:00
parent d91b804411
commit 06adbc3e49

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/gomodule/redigo/redis" "github.com/gomodule/redigo/redis"
"qoobing.com/gomod/log"
) )
var ( var (
@ -50,8 +51,11 @@ func NewCache[T any](getter Getter[T], cfg Config) Cacher[T] {
if cfg.UseRedisCache && cfg.RedisCacheConn != nil { if cfg.UseRedisCache && cfg.RedisCacheConn != nil {
c.redisCache = new(redisCacher[T]) c.redisCache = new(redisCacher[T])
c.redisCache.rds = cfg.RedisCacheConn c.redisCache.rds = cfg.RedisCacheConn
c.redisCache.cachePrefix = cfg.RedisCacheKeyPrefix
c.redisCache.cacheSecond = time.Duration(cfg.RedisCacheLifetimeSecond) * time.Second 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 { } else if cfg.UseRedisCache {
panic("want to user redis cache, but redis.Conn is nil") 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 { type cacheItem[T any] struct {
Data T //cache data Data *T `json:"d"` //cache data
CreateTime time.Time //cache create time CreateTime time.Time `json:"t"` //cache create time
} }
type localCacher[T any] struct { type localCacher[T any] struct {
@ -69,7 +73,7 @@ type localCacher[T any] struct {
cacheSecond time.Duration cacheSecond time.Duration
} }
type redisCacher struct { type redisCacher[T any] struct {
rds redis.Conn rds redis.Conn
cachePrefix string cachePrefix string
cacheSecond time.Duration cacheSecond time.Duration
@ -81,42 +85,46 @@ type cacher[T any] struct {
redisCache *redisCacher[T] 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) var earliestCreateTime = time.Now().Add(-c.cacheSecond)
if a, ok := c.cacheItems[appid]; !ok { if a, ok := c.cacheItems[id]; !ok {
return nil, ErrAppNotExist return nil, ErrNotFound
} else if a.CreateTime.Before(earliestCreateTime) { } else if earliestCreateTime.Before(a.CreateTime) {
return a, nil return a.Data, nil
} else if ok {
delete(c.apps, appid)
return nil, ErrAppNotExist
} }
//TODO: cocurrent
delete(c.cacheItems, id)
return nil, ErrNotFound
} }
func (c *localCacher) SetIntoCache(id string, t *T) (err error) { func (c *localCacher[T]) SetIntoCache(id string, t *T) (err error) {
c.apps[id] = t c.cacheItems[id] = &cacheItem[T]{
Data: t,
CreateTime: time.Now(),
}
fmt.Println("cacheItems", c.cacheItems)
return nil return nil
} }
func (c *redisCacher) GetFromCache(id string) (*T, error) { func (c *redisCacher[T]) GetFromCache(id string) (*T, error) {
var ( var (
rds = c.rds rds = c.rds
redisKey = fmt.Sprintf("appconfig-by-appid:%s", appid) redisKey = c.cachePrefix + id
redisValue = "" redisValue = ""
err error = nil err error = nil
) )
// Step 1. read from redis // Step 1. read from redis
if redisValue, err = redis.String(rds.Do("GET", redisKey)); err != nil { if redisValue, err = redis.String(rds.Do("GET", redisKey)); err != nil {
log.Infof("get appconfig cache failed: 'redis return<%s>'", err.Error()) log.Infof("get cache failed: 'redis return<%s>'", err.Error())
return nil, ErrAppNotExist return nil, ErrNotFound
} }
// Step 2. decode from string // Step 2. decode from string
a := &App{} a := &cacheItem[T]{}
if err = json.Unmarshal([]byte(redisValue), a); err != nil { if err = json.Unmarshal([]byte(redisValue), a); err != nil {
log.Errorf("get appconfig cache failed: 'json unmarshal failed <%s>'", err.Error()) log.Errorf("get cache failed: 'json unmarshal failed <%s>'", err.Error())
return nil, ErrAppNotExist return nil, ErrNotFound
} }
// Step 3. check expire time // Step 3. check expire time
@ -124,32 +132,33 @@ func (c *redisCacher) GetFromCache(id string) (*T, error) {
if d.Nanoseconds() > 0 { if d.Nanoseconds() > 0 {
var earliestCreateTime = time.Now().Add(-c.cacheSecond) var earliestCreateTime = time.Now().Add(-c.cacheSecond)
if a.CreateTime.Before(earliestCreateTime) { if a.CreateTime.Before(earliestCreateTime) {
log.Infof("app(%s) is in redis cache but expired", appid) log.Infof("app(%s) is in redis cache but expired", id)
return nil, ErrAppNotExist 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 ( var (
rds = c.rds rds = c.rds
redisKey = fmt.Sprintf("appconfig-by-appid:%s", appid) redisKey = c.cachePrefix + id
redisValue = []byte{} redisValue = []byte{}
err error = nil err error = nil
) )
// Step 1. decode from string // Step 1. decode from string
var a = cacheItem[T]{Data: t, CreateTime: time.Now()}
if redisValue, err = json.Marshal(a); err != nil { if redisValue, err = json.Marshal(a); err != nil {
log.Errorf("set appconfig cache failed: 'json marshal failed <%s>'", err.Error()) log.Errorf("set cache failed: 'json marshal failed <%s>'", err.Error())
return errors.New("set appconfig cache failed: 'marshal failed'") return errors.New("set cache failed: 'marshal failed'")
} }
// Step 2. read from redis // Step 2. read from redis
if _, err = rds.Do("SET", redisKey, string(redisValue)); err != nil { if _, err = rds.Do("SET", redisKey, string(redisValue)); err != nil {
log.Errorf("set appconfig cache failed: 'redis failed <%s>'", err.Error()) log.Errorf("set cache failed: 'redis failed <%s>'", err.Error())
return errors.New("set appconfig cache failed: 'redis failed'") return errors.New("set cache failed: 'redis failed'")
} }
return nil return nil
@ -174,27 +183,30 @@ func (c *cacher[T]) GetFromCache(id string) (dat *T, err error) {
// Step 3. get from [redis] // Step 3. get from [redis]
if c.redisCache != nil { if c.redisCache != nil {
if dat, err := c.redisCache.GetFromCache(id); err == 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) log.Infof("get cache(id:%s) from redisCacher success", id)
return dat, nil return dat, nil
} else if c.getter == 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) "trade as not found. may be you forgot set in db ???", id)
return nil, ErrAppNotExist return nil, ErrNotFound
} }
} }
if c.getter == nil { 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) "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)] // Step 4. get from [storager(database or somewhere)]
dat, err := c.getter.GetAppId(id) dat, err = c.getter.GetById(id)
if err != nil { if err != nil {
log.Errorf("cache(id:%s) is not in database, something error: %s", id, err) 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) log.Infof("get cache(id:%s) from exportStorager(maybe database) success", id)
// Step 5. set to cacha // 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 c.localCache != nil {
if err = c.localCache.SetIntoCache(id, dat); err != nil { if err = c.localCache.SetIntoCache(id, dat); err != nil {
log.Infof("set data into localCache failed, err:%s", err) 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 c.redisCache != nil {
if err = c.redisCache.SetIntoCache(id, dat); err != nil { if err = c.redisCache.SetIntoCache(id, dat); err != nil {
log.Infof("set data into redisCache failed, err:%s", err) log.Infof("set data into redisCache failed, err:%s", err)
} else {
log.Debugf("success set data into redisCache, id: %s", id)
} }
} }
return nil return nil