Skip to content

Commit

Permalink
refactor: split redis and memory cache
Browse files Browse the repository at this point in the history
  • Loading branch information
trim21 committed Sep 6, 2022
1 parent 64c800f commit e3048d1
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 114 deletions.
8 changes: 4 additions & 4 deletions etc/mock.task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ tasks:

cache:
sources:
- internal/cache/interface.go
- internal/cache/redis.go
- ./internal/pkg/tools/go.mod
generates:
- internal/mocks/Cache.go
- internal/mocks/RedisCache.go
cmds:
- task: base-mock
vars:
SRC_DIR: ./internal/cache
INTERFACE: "Cache"
MOCK_STRUCT: Cache
INTERFACE: "RedisCache"
MOCK_STRUCT: RedisCache

session-manager:
sources:
Expand Down
26 changes: 13 additions & 13 deletions internal/auth/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ import (
const TokenTypeOauthToken = 0
const TokenTypeAccessToken = 1

func NewService(repo domain.AuthRepo, user domain.UserRepo, logger *zap.Logger, c cache.Cache) domain.AuthService {
func NewService(repo domain.AuthRepo, user domain.UserRepo, logger *zap.Logger, c cache.RedisCache) domain.AuthService {
return service{
localCache: cache.NewMemoryCache(),
cache: c,
repo: repo,
log: logger.Named("auth.Service"),
user: user,
permCache: cache.NewMemoryCache(),
cache: c,
repo: repo,
log: logger.Named("auth.Service"),
user: user,
}
}

type service struct {
localCache cache.Cache
cache cache.Cache
repo domain.AuthRepo
user domain.UserRepo
log *zap.Logger
permCache *cache.MemoryCache
cache cache.RedisCache
repo domain.AuthRepo
user domain.UserRepo
log *zap.Logger
}

func (s service) GetByID(ctx context.Context, userID model.UserID) (domain.Auth, error) {
Expand Down Expand Up @@ -181,7 +181,7 @@ func preProcessPassword(s string) [32]byte {
func (s service) getPermission(ctx context.Context, id model.UserGroupID) (domain.Permission, error) {
var p domain.Permission
key := strconv.FormatUint(uint64(id), 10)
ok, err := s.localCache.Get(ctx, key, &p)
ok, err := s.permCache.Get(ctx, key, &p)
if err != nil {
return domain.Permission{}, errgo.Wrap(err, "read cache")
}
Expand All @@ -195,7 +195,7 @@ func (s service) getPermission(ctx context.Context, id model.UserGroupID) (domai
return domain.Permission{}, errgo.Wrap(err, "AuthRepo.GetPermission")
}

_ = s.localCache.Set(ctx, key, p, time.Minute)
_ = s.permCache.Set(ctx, key, p, time.Minute)

return p, nil
}
Expand Down
5 changes: 3 additions & 2 deletions internal/auth/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ import (
"go.uber.org/zap"

"github.com/bangumi/server/internal/auth"
"github.com/bangumi/server/internal/cache"
"github.com/bangumi/server/internal/domain"
"github.com/bangumi/server/internal/mocks"
"github.com/bangumi/server/internal/model"
"github.com/bangumi/server/internal/pkg/test"
)

func getService() domain.AuthService {
return auth.NewService(nil, nil, zap.NewNop(), test.NopCache())
return auth.NewService(nil, nil, zap.NewNop(), cache.NewNoop())
}

func TestService_ComparePassword(t *testing.T) {
Expand All @@ -53,7 +54,7 @@ func TestService_GetByToken(t *testing.T) {

var u = mocks.NewUserRepo(t)

s := auth.NewService(m, u, zap.NewNop(), test.NopCache())
s := auth.NewService(m, u, zap.NewNop(), cache.NewNoop())

a, err := s.GetByToken(context.Background(), test.TreeHoleAccessToken)
require.NoError(t, err)
Expand Down
26 changes: 10 additions & 16 deletions internal/cache/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@ import (
"github.com/bangumi/server/internal/pkg/errgo"
)

// NewMemoryCache return an in-memory cache.
// This cache backend should be used to cache limited-sized entries like user group permission rule.
func NewMemoryCache() Cache {
return &memCache{}
// NewMemoryCache 不对缓存的对象进行序列化,直接用 [sync.Map] 保存在内存里。
//
// 过期的缓存不会从内存中自动回收,不能用来缓存值空间非常大的数据如条目或用户,
// 用于缓存用户组权限这样的值空间比较小的数据。
func NewMemoryCache() *MemoryCache {
return &MemoryCache{}
}

var errCacheNotSameType = errors.New("cached item have is not same type as expected result")

// memCache store data in memory,
// MemoryCache store data in memory,
// will be used to cache user group permission rule.
type memCache struct {
type MemoryCache struct {
m sync.Map
}

Expand All @@ -44,7 +46,7 @@ type cacheItem struct {
Dead time.Time
}

func (c *memCache) Get(_ context.Context, key string, value any) (bool, error) {
func (c *MemoryCache) Get(_ context.Context, key string, value any) (bool, error) {
v, ok := c.m.Load(key)
if !ok {
return ok, nil
Expand Down Expand Up @@ -77,19 +79,11 @@ func (c *memCache) Get(_ context.Context, key string, value any) (bool, error) {
return true, nil
}

func (c *memCache) Set(_ context.Context, key string, value any, ttl time.Duration) error {
func (c *MemoryCache) Set(_ context.Context, key string, value any, ttl time.Duration) error {
c.m.Store(key, cacheItem{
Value: value,
Dead: time.Now().Add(ttl),
})

return nil
}

func (c *memCache) Del(ctx context.Context, keys ...string) error {
for _, key := range keys {
c.m.Delete(key)
}

return nil
}
13 changes: 0 additions & 13 deletions internal/cache/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,3 @@ func Test_MemCache_Expired(t *testing.T) {
require.NoError(t, err)
require.False(t, ok)
}

func Test_MemCache_Del(t *testing.T) {
t.Parallel()
var key = "K" + t.Name()
m := cache.NewMemoryCache()

require.NoError(t, m.Set(context.Background(), key, MemCacheTestItem{}, time.Hour))
require.NoError(t, m.Del(context.Background(), key))

ok, err := m.Get(context.Background(), key, &MemCacheTestItem{})
require.NoError(t, err)
require.False(t, ok)
}
21 changes: 16 additions & 5 deletions internal/cache/interface.go → internal/cache/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@ import (
"time"
)

type Cache interface {
Get(ctx context.Context, key string, value any) (bool, error)
Set(ctx context.Context, key string, value any, ttl time.Duration) error
Del(ctx context.Context, keys ...string) error
// SetMany(ctx context.Context, keys string, values []any, ttl time.Duration) error
func NewNoop() RedisCache {
return noop{}
}

type noop struct{}

func (n noop) Get(context.Context, string, any) (bool, error) {
return false, nil
}

func (n noop) Set(context.Context, string, any, time.Duration) error {
return nil
}

func (n noop) Del(context.Context, ...string) error {
return nil
}
15 changes: 10 additions & 5 deletions internal/cache/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,23 @@ import (
"github.com/bangumi/server/internal/pkg/logger"
)

type RedisCache interface {
Get(ctx context.Context, key string, value any) (bool, error)
Set(ctx context.Context, key string, value any, ttl time.Duration) error
Del(ctx context.Context, keys ...string) error
// SetMany(ctx context.Context, keys string, values []any, ttl time.Duration) error
}

// NewRedisCache create a redis backed cache.
func NewRedisCache(cli *redis.Client) Cache {
func NewRedisCache(cli *redis.Client) RedisCache {
return redisCache{r: cli}
}

type redisCache struct {
r *redis.Client
}

func (c redisCache) Get(
ctx context.Context, key string, value any) (bool, error) {
func (c redisCache) Get(ctx context.Context, key string, value any) (bool, error) {
raw, err := c.r.Get(ctx, key).Bytes()
if err != nil {
if err == redis.Nil {
Expand All @@ -57,8 +63,7 @@ func (c redisCache) Get(
return true, nil
}

func (c redisCache) Set(
ctx context.Context, key string, value any, ttl time.Duration) error {
func (c redisCache) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
b, err := json.MarshalWithOption(value, json.DisableHTMLEscape())
if err != nil {
return errgo.Wrap(err, "json")
Expand Down
2 changes: 1 addition & 1 deletion internal/cache/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type RedisCacheTestItem struct {

const key = "news_redis_cache"

func mockedCache() (cache.Cache, redismock.ClientMock) {
func mockedCache() (cache.RedisCache, redismock.ClientMock) {
db, mock := redismock.NewClientMock()
c := cache.NewRedisCache(db)

Expand Down
4 changes: 2 additions & 2 deletions internal/ctrl/ctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

func New(
episode domain.EpisodeRepo,
cache cache.Cache,
cache cache.RedisCache,
subject domain.SubjectRepo,
person domain.PersonRepo,
character domain.CharacterRepo,
Expand Down Expand Up @@ -66,7 +66,7 @@ func New(

type Ctrl struct {
log *zap.Logger
cache cache.Cache
cache cache.RedisCache

tx dal.Transaction
dam dam.Dam
Expand Down
Loading

0 comments on commit e3048d1

Please sign in to comment.