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
33 changes: 12 additions & 21 deletions pkg/cache/xfreecache/cache_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package xfreecache

import (
"fmt"

"encoding/json"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -19,39 +18,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 +53,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
103 changes: 103 additions & 0 deletions pkg/cache/xfreecache/v2/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package xfreecache

import (
"fmt"

"github.com/douyu/jupiter/pkg/xlog"
"github.com/samber/lo"
"go.uber.org/zap"
)

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, len(ids))
pool := getPool[V]()
for _, id := range ids {
cacheKey := c.getKey(key, id)
if resT, innerErr := c.GetCacheData(cacheKey); innerErr == nil && resT != nil {
var value V
// 反序列化
value, err = unmarshalWithPool[V](resT, pool)
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
}
// 序列化
data, err = 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