cache.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package vip
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/pkg/errors"
  7. "go-common/app/service/live/xuser/model"
  8. "go-common/library/cache/redis"
  9. "go-common/library/log"
  10. "go-common/library/net/metadata"
  11. "time"
  12. )
  13. // redis cache
  14. const (
  15. _userInfoRedisKey = "us:infoo_v2:%d" // 用户缓存key prefix
  16. _vipFieldName = "vip" // v3 vip attr field
  17. _levelFieldName = "level" // v2 level attr field, Todo: remove level attr
  18. _userExpired = 86400 // user cache expire time
  19. )
  20. type vipCache struct {
  21. Vip interface{} `json:"vip"`
  22. VipTime string `json:"vip_time"`
  23. Svip interface{} `json:"svip"`
  24. SvipTime string `json:"svip_time"`
  25. }
  26. // GetVipFromCache get user vip info from cache
  27. func (d *Dao) GetVipFromCache(ctx context.Context, uid int64) (info *model.VipInfo, err error) {
  28. conn := d.redis.Get(ctx)
  29. defer conn.Close()
  30. reply, err := redis.String(conn.Do("HGET", getUserCacheKey(uid), _vipFieldName))
  31. if err != nil {
  32. if err == redis.ErrNil {
  33. // key or field not exists, return nil, nil, back to db
  34. log.Info("[dao.vip.cache|GetVipFromCache] cache key or field not exists err(%v), uid(%d)", err, uid)
  35. return nil, nil
  36. }
  37. log.Error("[dao.vip.cache|GetVipFromCache] hget error(%v), uid(%d)", err, uid)
  38. return
  39. }
  40. if reply == "" {
  41. return nil, nil
  42. }
  43. // ===== begin eat others' dog food =====
  44. // 1.兼容缓存中vip/svip可能是int or string的问题
  45. rawInfo := &vipCache{}
  46. if err = json.Unmarshal([]byte(reply), rawInfo); err != nil {
  47. log.Error("[dao.vip.cache|GetVipFromCache] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
  48. err, uid, reply)
  49. // parse cache json error, return nil, nil, back to db and restore cache
  50. return nil, nil
  51. }
  52. if info, err = d.formatVipCache(rawInfo); err != nil {
  53. log.Error("[dao.vip.cache|GetVipFromCache] format rawInfo error(%v), uid(%d), reply(%s)", err, uid, reply)
  54. return nil, nil
  55. }
  56. // 2.注意!!! cache里的vip_time/svip_time不一定正确,可能含有已经过期的time
  57. currentTime := time.Now().Unix()
  58. // vip time
  59. if info.Vip, err = d.checkVipTime(info.VipTime, info.Vip, currentTime); err != nil {
  60. log.Error("[dao.vip.cache|GetVipFromCache] check vip time error(%v), uid(%d), info(%v), reply(%s)",
  61. err, uid, info, reply)
  62. return nil, nil
  63. }
  64. if info.Svip, err = d.checkVipTime(info.SvipTime, info.Svip, currentTime); err != nil {
  65. log.Error("[dao.vip.cache|GetVipFromCache] check svip time error(%v), uid(%d), info(%v), reply(%s)",
  66. err, uid, info, reply)
  67. return nil, nil
  68. }
  69. // ===== end =====
  70. return
  71. }
  72. // formatVipCache 转换vip/svip的格式
  73. func (d *Dao) formatVipCache(info *vipCache) (v *model.VipInfo, err error) {
  74. v = &model.VipInfo{
  75. VipTime: info.VipTime,
  76. SvipTime: info.SvipTime,
  77. }
  78. if v.Vip, err = toInt(info.Vip); err != nil {
  79. return
  80. }
  81. if v.Svip, err = toInt(info.Svip); err != nil {
  82. return
  83. }
  84. // format info struct
  85. v = d.initInfo(v)
  86. return
  87. }
  88. // checkVipTime 检查缓存中vip_time/svip_time是否过期
  89. func (d *Dao) checkVipTime(t string, f int, compare int64) (int, error) {
  90. if t == model.TimeEmpty {
  91. if f != 0 {
  92. return 0, errors.New("empty time with not zero flag.")
  93. }
  94. } else {
  95. vt, err := time.Parse(model.TimeNano, t)
  96. if err != nil {
  97. return 0, errors.New("time parse error.")
  98. }
  99. if vt.Unix() <= compare {
  100. return 0, nil
  101. }
  102. }
  103. return f, nil
  104. }
  105. // SetVipCache set vip to cache
  106. func (d *Dao) SetVipCache(ctx context.Context, uid int64, info *model.VipInfo) (err error) {
  107. var vipJson []byte
  108. conn := d.redis.Get(ctx)
  109. key := getUserCacheKey(uid)
  110. defer conn.Close()
  111. // format info struct
  112. info = d.initInfo(info)
  113. // format info json string
  114. vipJson, err = json.Marshal(info)
  115. if err != nil {
  116. log.Error("[dao.vip.cache|SetVipCache] json.Marshal error(%v), uid(%d), info(%v)", err, uid, info)
  117. // if marshal error, clear cache
  118. goto CLEAR
  119. }
  120. _, err = conn.Do("HSET", key, _vipFieldName, string(vipJson))
  121. if err != nil {
  122. log.Error("[dao.vip.cache|SetVipCache] HSET error(%v), uid(%d), info(%v)", err, uid, info)
  123. // if hset error, clear cache
  124. goto CLEAR
  125. }
  126. _, err = conn.Do("EXPIRE", key, _userExpired)
  127. if err != nil {
  128. log.Error("[dao.vip.cache|SetVipCache] EXPIRE error(%v), uid(%d), info(%v)", err, uid, info)
  129. // if set expire error, clear cache
  130. goto CLEAR
  131. }
  132. return
  133. CLEAR:
  134. log.Error("[dao.vip.cache|SetVipCache] set error, aysnc clear, uid(%d), info(%v)", uid, info)
  135. go d.ClearCache(metadata.WithContext(ctx), uid)
  136. return
  137. }
  138. // ClearCache clear user's vip and level field cache
  139. // Todo: remove level attr
  140. func (d *Dao) ClearCache(ctx context.Context, uid int64) (err error) {
  141. conn := d.redis.Get(ctx)
  142. defer conn.Close()
  143. key := getUserCacheKey(uid)
  144. _, err = conn.Do("HDEL", key, _vipFieldName, _levelFieldName)
  145. if err != nil {
  146. err = errors.Wrapf(err, "conn.Do(HDEL, %s, %s, %s)", key, _vipFieldName, _levelFieldName)
  147. log.Error("[dao.vip.cache|ClearCache] hdel uid(%d) vip and level attr err(%v)", uid, err)
  148. }
  149. return
  150. }
  151. func getUserCacheKey(uid int64) string {
  152. return fmt.Sprintf(_userInfoRedisKey, uid)
  153. }