Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(freecache): v2 finish #702

Merged
merged 14 commits into from
Feb 17, 2023
30 changes: 11 additions & 19 deletions pkg/cache/xfreecache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,33 @@ func TestLocalCache(t *testing.T) {
missCount := 0

tests := []struct {
name string
stu Student
stu Student
}{
{
name: "Student 1",
stu: Student{
Age: 1,
Name: "Student 1",
},
},
{
name: "Student 2",
stu: Student{
Age: 2,
Name: "Student 2",
},
},
{
name: "Student 1",
stu: Student{
Age: 1,
Name: "Student 1",
},
},
{
name: "Student 3",
stu: Student{
Age: 1,
Name: "Student 3",
},
},
{
name: "Student 2",
stu: Student{
Age: 2,
Name: "Student 2",
Expand All @@ -60,19 +54,17 @@ func TestLocalCache(t *testing.T) {
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
key := fmt.Sprintf("%d-%s", tt.stu.Age, tt.stu.Name)
result, _ := oneCache.GetAndSetCacheData(key, func() ([]byte, error) {
missCount++
fmt.Println("local cache miss hit")
ret, _ := json.Marshal(tt.stu)
return ret, nil
})
ret := Student{}
_ = json.Unmarshal(result, &ret)
fmt.Println(ret)
assert.Equalf(t, tt.stu, ret, "GetAndSetCacheData(%v) cache value error", key)
key := fmt.Sprintf("%d-%s", tt.stu.Age, tt.stu.Name)
result, _ := oneCache.GetAndSetCacheData(key, func() ([]byte, error) {
missCount++
fmt.Println("local cache miss hit")
ret, _ := json.Marshal(tt.stu)
return ret, nil
})
ret := Student{}
_ = json.Unmarshal(result, &ret)
fmt.Println(ret)
assert.Equalf(t, tt.stu, ret, "GetAndSetCacheData(%v) cache value error", key)
}
assert.Equalf(t, missCount, 3, "GetAndSetCacheData miss count error")
}
Expand Down
116 changes: 116 additions & 0 deletions pkg/cache/xfreecache/v2/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package xfreecache

import (
"encoding/json"
"fmt"
"github.com/douyu/jupiter/pkg/xlog"
"github.com/samber/lo"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"reflect"
)

type storage interface {
// SetCacheData 设置缓存数据 key:缓存key data:缓存数据
SetCacheData(key string, data []byte) (err error)
// GetCacheData 存储缓存数据 key:缓存key data:缓存数据
GetCacheData(key string) (data []byte, err error)
}

type cache[K comparable, V any] struct {
storage
}

// GetAndSetCacheData 获取缓存后数据
func (c *cache[K, V]) GetAndSetCacheData(key string, id K, fn func() (V, error)) (value V, err error) {
resMap, err := c.GetAndSetCacheMap(key, []K{id}, func([]K) (map[K]V, error) {
innerVal, innerErr := fn()
return map[K]V{id: innerVal}, innerErr
})
value = resMap[id]
return
}

// GetAndSetCacheMap 获取缓存后数据 map形式
func (c *cache[K, V]) GetAndSetCacheMap(key string, ids []K, fn func([]K) (map[K]V, error)) (v map[K]V, err error) {
args := []zap.Field{zap.Any("key", key), zap.Any("ids", ids)}

v = make(map[K]V)

// id去重
ids = lo.Uniq(ids)
idsNone := make([]K, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里可以考虑预分配len(ids)的内存

for _, id := range ids {
cacheKey := c.getKey(key, id)
if resT, innerErr := c.GetCacheData(cacheKey); innerErr == nil && resT != nil {
var value V
if msg, ok := any(value).(proto.Message); ok { // Constrained to proto.Message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以考虑封装下unmarshal和marshal的方法,参数是any

// Peek the type inside T (as T= *SomeProtoMsgType)
msgType := reflect.TypeOf(msg).Elem()
// Make a new one, and throw it back into T
msg = reflect.New(msgType).Interface().(proto.Message)

err = proto.Unmarshal(resT, msg)
value = msg.(V)
} else {
err = json.Unmarshal(resT, &value)
}
if err != nil {
return
}
v[id] = value
continue
}
idsNone = append(idsNone, id)
}

if len(idsNone) == 0 {
return
}

// 执行函数
resMap, err := fn(idsNone)
if err != nil {
xlog.Jupiter().Error("GetAndSetCacheMap doMap", append(args, zap.Error(err))...)
return
}

// 填入返回中
for k, value := range resMap {
v[k] = value
}

// 写入缓存
for _, id := range idsNone {
var (
cacheData V
data []byte
)

if val, ok := v[id]; ok {
cacheData = val
}
if msg, ok := any(cacheData).(proto.Message); ok {
data, err = proto.Marshal(msg)
} else {
data, err = json.Marshal(cacheData)
}

if err != nil {
xlog.Jupiter().Error("GetAndSetCacheMap Marshal", append(args, zap.Error(err))...)
return
}

cacheKey := c.getKey(key, id)
err = c.SetCacheData(cacheKey, data)
if err != nil {
xlog.Jupiter().Error("GetAndSetCacheMap setCacheData", append(args, zap.Error(err))...)
return
}
}
return
}

func (c *cache[K, V]) getKey(key string, id K) string {
return fmt.Sprintf("%s:%v", key, id)
}
65 changes: 65 additions & 0 deletions pkg/cache/xfreecache/v2/cache_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package xfreecache

import (
"strconv"
"testing"
)

func BenchmarkLocalCache_GetCacheData(b *testing.B) {
localCache := New[string, Student](DefaultConfig())

b.Run("read", func(b *testing.B) {
student := Student{10, "student1"}
for i := 0; i < b.N; i++ {
_, _ = localCache.GetAndSetCacheData("mytest", student.Name, func() (Student, error) {
res := student
return res, nil
})
}
})

b.Run("read & write", func(b *testing.B) {
for i := 0; i < b.N; i++ {
student := Student{10, "student" + strconv.Itoa(i)}
_, _ = localCache.GetAndSetCacheData("mytest", student.Name, func() (Student, error) {
res := student
return res, nil
})
}
})
}

func BenchmarkLocalCache_GetCacheMap(b *testing.B) {

localCache := New[int64, int64](DefaultConfig())

b.Run("read", func(b *testing.B) {
uidList := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for i := 0; i < b.N; i++ {
_, _ = localCache.GetAndSetCacheMap("mytest2", uidList, func(in []int64) (map[int64]int64, error) {
res := make(map[int64]int64)
for _, uid := range in {
res[uid] = uid
}
return res, nil
})
}
})

b.Run("read & write", func(b *testing.B) {
uidList := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for i := 0; i < b.N; i++ {
uidListT := make([]int64, 0, 10)
for _, uid := range uidList {
uidListT = append(uidListT, uid+int64(i))
}
_, _ = localCache.GetAndSetCacheMap("mytest2", uidListT, func(in []int64) (map[int64]int64, error) {
res := make(map[int64]int64)
for _, uid := range in {
res[uid] = uid
}
return res, nil
})
}
})
}
Loading