Skip to content

Commit

Permalink
feat: 封装lru、byte数据、缓存组&添加测试用例
Browse files Browse the repository at this point in the history
  • Loading branch information
ithaiq committed Jan 13, 2022
1 parent c77df27 commit 912f6dc
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
25 changes: 25 additions & 0 deletions byte_value.go
@@ -0,0 +1,25 @@
package thqcache

//ByteValue 数据格式封装
type ByteValue struct {
b []byte
}

func (v ByteValue) Len() int {
return len(v.b)
}

func (v ByteValue) ByteSlice() []byte {
return cloneBytes(v.b)
}

func (v ByteValue) String() string {
return string(v.b)
}

//cloneBytes 深拷贝防止修改b
func cloneBytes(b []byte) []byte {
c := make([]byte, len(b))
copy(c, b)
return c
}
35 changes: 35 additions & 0 deletions cache.go
@@ -0,0 +1,35 @@
package thqcache

import (
"github.com/ithaiq/thqcache/lru"
"sync"
)

//cache lru Cache封装 + 锁并发控制
type cache struct {
lru *lru.Cache
mu sync.Mutex
maxBytes int64
}

func (this *cache) add(key string, value ByteValue) {
this.mu.Lock()
defer this.mu.Unlock()
//懒加载 性能考虑
if this.lru == nil {
this.lru = lru.NewCache(this.maxBytes, nil)
}
this.lru.Add(key, value)
}

func (this *cache) get(key string) (value ByteValue, ok bool) {
this.mu.Lock()
defer this.mu.Unlock()
if this.lru == nil {
return
}
if v, ok := this.lru.Get(key); ok {
return v.(ByteValue), true
}
return
}
76 changes: 76 additions & 0 deletions thqcache.go
@@ -0,0 +1,76 @@
package thqcache

import (
"fmt"
"sync"
)

type Getter interface {
Get(key string) ([]byte, error)
}

//接口型函数
//方便使用者在调用时既能够传入函数作为参数,也能够传入实现了该接口的结构体作为参数。

type GetterFunc func(key string) ([]byte, error)

func (f GetterFunc) Get(key string) ([]byte, error) {
return f(key)
}

type Group struct {
name string
getter Getter
c cache
}

var (
mu sync.RWMutex
groups = make(map[string]*Group)
)

func NewGroup(name string, maxBytes int64, getter Getter) *Group {
if getter == nil {
panic("nil Getter")
}
mu.Lock()
defer mu.Unlock()
g := &Group{
name: name,
getter: getter,
c: cache{maxBytes: maxBytes},
}
groups[name] = g
return g
}

func GetGroup(name string) *Group {
mu.RLock()
defer mu.RUnlock()
return groups[name]
}

func (this *Group) Add(key string, value ByteValue) {
this.c.add(key, value)
}

func (this *Group) Get(key string) (ByteValue, error) {
if key == "" {
return ByteValue{}, fmt.Errorf("key is empty")
}
if v, ok := this.c.get(key); ok {
return v, nil
}
return this.loadLocal(key)
}

//loadLocal 加载本地数据到缓存
func (this *Group) loadLocal(key string) (ByteValue, error) {
bytes, err := this.getter.Get(key)
if err != nil {
return ByteValue{}, err
}
value := ByteValue{b: cloneBytes(bytes)}
this.Add(key, value)
return value, nil
}
40 changes: 40 additions & 0 deletions thqcache_test.go
@@ -0,0 +1,40 @@
package thqcache

import (
"fmt"
"github.com/stretchr/testify/require"
"testing"
)

var db = map[string]string{
"thq1": "1",
"thq2": "2",
"thq3": "3",
}

func TestGroup_Get(t *testing.T) {
loadCount := make(map[string]int, len(db))
getter := func(key string) ([]byte, error) {
if v, ok := db[key]; ok {
if _, ok := loadCount[key]; !ok {
loadCount[key] = 0
}
loadCount[key]++
return []byte(v), nil
}
return nil, fmt.Errorf("%s not exist", key)
}

g := NewGroup("test", 2<<10, GetterFunc(getter))

for k, v := range db {
value, err := g.Get(k)
require.NoError(t, err)
require.Equal(t, value.String(), v)
_, err = g.Get(k)
require.NoError(t, err)
require.True(t, loadCount[k] == 1)
}
_, err := g.Get("no")
require.True(t, err != nil)
}

0 comments on commit 912f6dc

Please sign in to comment.