Skip to content

Commit

Permalink
[feat](新增set实现) 补充lru set实现 (#22)
Browse files Browse the repository at this point in the history
* [feat](新增set实现) 补充lru set实现

* 补充测试用例

* 调整比较方式、修改返回时的参数

* 修改set实现,预设size为redis存储大小同步

* 缩小set大小,优化代码
  • Loading branch information
Depravity-pig authored Oct 12, 2023
1 parent da8d024 commit cc85ba2
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 15 deletions.
54 changes: 50 additions & 4 deletions memory/lru/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"sync"
"time"

"github.com/ecodeclub/ekit/set"

"github.com/ecodeclub/ekit/list"

"github.com/ecodeclub/ecache"
Expand Down Expand Up @@ -138,7 +140,7 @@ func (c *Cache) LPush(ctx context.Context, key string, val ...any) (int64, error
result.Val, ok = c.client.Get(key)
if !ok {
l := &list.ConcurrentList[ecache.Value]{
List: list.NewLinkedListOf[ecache.Value](c.anySliceToValueSlice(val)),
List: list.NewLinkedListOf[ecache.Value](c.anySliceToValueSlice(val...)),
}
c.client.Add(key, l)
return int64(l.Len()), nil
Expand Down Expand Up @@ -188,12 +190,56 @@ func (c *Cache) LPop(ctx context.Context, key string) (val ecache.Value) {
}

func (c *Cache) SAdd(ctx context.Context, key string, members ...any) (int64, error) {
// TODO
return 0, nil
c.lock.Lock()
defer c.lock.Unlock()

var (
ok bool
result = ecache.Value{}
)
result.Val, ok = c.client.Get(key)
if !ok {
result.Val = set.NewMapSet[any](8)
}

s, ok := result.Val.(set.Set[any])
if !ok {
return 0, errors.New("当前key已存在不是set类型")
}

for _, value := range members {
s.Add(value)
}
c.client.Add(key, s)

return int64(len(s.Keys())), nil
}

func (c *Cache) SRem(ctx context.Context, key string, members ...any) (val ecache.Value) {
// TODO
c.lock.Lock()
defer c.lock.Unlock()

result, ok := c.client.Get(key)
if !ok {
val.Err = errs.ErrKeyNotExist
return
}

s, ok := result.(set.Set[any])
if !ok {
val.Err = errors.New("当前key已存在不是set类型")
return
}

var rems = make([]any, 0, cap(members))
for _, member := range members {
if s.Exist(member) {
rems = append(rems, member)
s.Delete(member)
}
}

val.Val = rems
return
}

Expand Down
200 changes: 189 additions & 11 deletions memory/lru/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ package lru
import (
"context"
"errors"
"reflect"
"testing"
"time"

"github.com/ecodeclub/ekit/set"

"github.com/ecodeclub/ecache"
"github.com/ecodeclub/ecache/internal/errs"
"github.com/ecodeclub/ekit/list"
Expand Down Expand Up @@ -359,7 +362,7 @@ func TestCache_LPush(t *testing.T) {
after func(t *testing.T)

key string
val string
val []any
wantVal int64
wantErr error
}{
Expand All @@ -370,9 +373,19 @@ func TestCache_LPush(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: "hello ecache",
val: []any{"hello ecache"},
wantVal: 1,
},
{
name: "lpush multiple value",
before: func(t *testing.T) {},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello ecache", "hello world"},
wantVal: 2,
},
{
name: "lpush value exists",
before: func(t *testing.T) {
Expand All @@ -387,7 +400,7 @@ func TestCache_LPush(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: "hello world",
val: []any{"hello world"},
wantVal: 2,
},
{
Expand All @@ -399,7 +412,7 @@ func TestCache_LPush(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: "hello ecache",
val: []any{"hello ecache"},
wantErr: errors.New("当前key不是list类型"),
},
}
Expand All @@ -411,7 +424,7 @@ func TestCache_LPush(t *testing.T) {
c := NewCache(lru)

tc.before(t)
length, err := c.LPush(ctx, tc.key, tc.val)
length, err := c.LPush(ctx, tc.key, tc.val...)
assert.Equal(t, tc.wantVal, length)
assert.Equal(t, tc.wantErr, err)
tc.after(t)
Expand Down Expand Up @@ -516,6 +529,171 @@ func TestCache_LPop(t *testing.T) {
}
}

func TestCache_SAdd(t *testing.T) {
evictCounter := 0
onEvicted := func(key string, value any) {
evictCounter++
}
lru, err := simplelru.NewLRU[string, any](5, onEvicted)
assert.NoError(t, err)

testCase := []struct {
name string
before func(t *testing.T)
after func(t *testing.T)

key string
val []any
wantVal int64
wantErr error
}{
{
name: "sadd value",
before: func(t *testing.T) {},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello ecache", "hello world"},
wantVal: 2,
},
{
name: "sadd value exist",
before: func(t *testing.T) {
s := set.NewMapSet[any](8)
s.Add("hello world")

assert.Equal(t, false, lru.Add("test", s))
},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello ecache"},
wantVal: 2,
},
{
name: "sadd value type err",
before: func(t *testing.T) {
assert.Equal(t, false, lru.Add("test", "string"))
},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello"},
wantErr: errors.New("当前key已存在不是set类型"),
},
}

for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
c := NewCache(lru)

tc.before(t)
val, err := c.SAdd(ctx, tc.key, tc.val...)
assert.Equal(t, tc.wantVal, val)
assert.Equal(t, tc.wantErr, err)
tc.after(t)
})
}
}

func TestCache_SRem(t *testing.T) {
evictCounter := 0
onEvicted := func(key string, value any) {
evictCounter++
}
lru, err := simplelru.NewLRU[string, any](5, onEvicted)
assert.NoError(t, err)

testCase := []struct {
name string
before func(t *testing.T)
after func(t *testing.T)

key string
val []any
wantVal []any
wantErr error
}{
{
name: "srem value",
before: func(t *testing.T) {
s := set.NewMapSet[any](8)

s.Add("hello world")
s.Add("hello ecache")

assert.Equal(t, false, lru.Add("test", s))
},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello world"},
wantVal: []any{"hello world"},
},
{
name: "srem value ignore",
before: func(t *testing.T) {
s := set.NewMapSet[any](8)
s.Add("hello world")

assert.Equal(t, false, lru.Add("test", s))
},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello ecache"},
wantVal: []any{},
},
{
name: "srem value nil",
before: func(t *testing.T) {},
after: func(t *testing.T) {},
key: "test",
val: []any{"hello world"},
wantErr: errs.ErrKeyNotExist,
},
{
name: "srem value type error",
before: func(t *testing.T) {
assert.Equal(t, false, lru.Add("test", int64(1)))
},
after: func(t *testing.T) {
assert.Equal(t, true, lru.Remove("test"))
},
key: "test",
val: []any{"hello world"},
wantErr: errors.New("当前key已存在不是set类型"),
},
}

for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
c := NewCache(lru)

tc.before(t)
val := c.SRem(ctx, tc.key, tc.val...)
defer tc.after(t)
if val.Err != nil {
assert.Equal(t, tc.wantErr, val.Err)
return
}

result, ok := val.Val.([]any)
assert.Equal(t, true, ok)
assert.Equal(t, true, reflect.DeepEqual(tc.wantVal, result))
})
}
}

func TestCache_IncrBy(t *testing.T) {
evictCounter := 0
onEvicted := func(key string, value any) {
Expand All @@ -524,7 +702,7 @@ func TestCache_IncrBy(t *testing.T) {
lru, err := simplelru.NewLRU[string, any](5, onEvicted)
assert.NoError(t, err)

testCache := []struct {
testCase := []struct {
name string
before func(t *testing.T)
after func(t *testing.T)
Expand Down Expand Up @@ -570,7 +748,7 @@ func TestCache_IncrBy(t *testing.T) {
},
}

for _, tc := range testCache {
for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
Expand All @@ -593,7 +771,7 @@ func TestCache_DecrBy(t *testing.T) {
lru, err := simplelru.NewLRU[string, any](5, onEvicted)
assert.NoError(t, err)

testCache := []struct {
testCase := []struct {
name string
before func(t *testing.T)
after func(t *testing.T)
Expand Down Expand Up @@ -639,7 +817,7 @@ func TestCache_DecrBy(t *testing.T) {
},
}

for _, tc := range testCache {
for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
Expand All @@ -662,7 +840,7 @@ func TestCache_IncrByFloat(t *testing.T) {
lru, err := simplelru.NewLRU[string, any](5, onEvicted)
assert.NoError(t, err)

testCache := []struct {
testCase := []struct {
name string
before func(t *testing.T)
after func(t *testing.T)
Expand Down Expand Up @@ -708,7 +886,7 @@ func TestCache_IncrByFloat(t *testing.T) {
},
}

for _, tc := range testCache {
for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
Expand Down

0 comments on commit cc85ba2

Please sign in to comment.