This commit is contained in:
xing 2023-02-02 22:56:09 +08:00
parent 490abf0bf6
commit 8ecf8bf94e
4 changed files with 649 additions and 10 deletions

18
cache/map.go vendored
View File

@ -21,12 +21,16 @@ func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
m.cacheFunc = fn m.cacheFunc = fn
} }
func (m *MapCache[K, V]) Ttl(ctx context.Context, k K) time.Duration {
return m.data.Ttl(ctx, k, m.expireTime)
}
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) { func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
tt := m.data.Ttl(ctx, k, m.expireTime) tt := m.data.Ttl(ctx, k, m.expireTime)
if tt <= 0 { if tt <= 0 {
return return
} }
return time.Now().Add(m.data.Ttl(ctx, k, m.expireTime)) return time.Now().Add(m.data.Ttl(ctx, k, m.expireTime)).Add(-m.expireTime)
} }
func (m *MapCache[K, V]) SetCacheBatchFn(fn func(...any) (map[K]V, error)) { func (m *MapCache[K, V]) SetCacheBatchFn(fn func(...any) (map[K]V, error)) {
@ -58,20 +62,20 @@ func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
} }
} }
func NewMemoryMapCacheByFn[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] { func NewMapCacheByFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
return &MapCache[K, V]{ return &MapCache[K, V]{
data: NewMemoryMapCache[K, V](), mux: sync.Mutex{},
cacheFunc: fn, cacheFunc: fn,
expireTime: expireTime, expireTime: expireTime,
mux: sync.Mutex{}, data: cacheType,
} }
} }
func NewMemoryMapCacheByBatchFn[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] { func NewMapCacheByBatchFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
r := &MapCache[K, V]{ r := &MapCache[K, V]{
data: NewMemoryMapCache[K, V](), mux: sync.Mutex{},
batchCacheFn: fn, batchCacheFn: fn,
expireTime: expireTime, expireTime: expireTime,
mux: sync.Mutex{}, data: cacheType,
} }
r.setCacheFn(fn) r.setCacheFn(fn)
return r return r

370
cache/map_test.go vendored Normal file
View File

@ -0,0 +1,370 @@
package cache
import (
"context"
"fmt"
"github.com/fthvgb1/wp-go/helper/slice"
"reflect"
"strings"
"testing"
"time"
)
var ca MapCache[string, string]
var fn func(a ...any) (string, error)
var batchFn func(a ...any) (map[string]string, error)
var ct context.Context
func init() {
fn = func(a ...any) (string, error) {
aa := a[1].(string)
return strings.Repeat(aa, 2), nil
}
ct = context.Background()
batchFn = func(a ...any) (map[string]string, error) {
arr := a[1].([]string)
return slice.SimpleToMap(arr, func(t string) string {
return strings.Repeat(t, 2)
}), nil
}
ca = *NewMemoryMapCacheByFn[string, string](fn, time.Second*2)
ca.SetCacheBatchFn(batchFn)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
_, _ = ca.GetCache(ct, "bb", time.Second, ct, "bb")
}
func TestMapCache_ClearExpired(t *testing.T) {
type args struct {
ct context.Context
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args{
ct: ct,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(ca.Get(ct, "aa"))
fmt.Println(ca.Get(ct, "bb"))
time.Sleep(time.Second * 3)
tt.m.ClearExpired(tt.args.ct)
fmt.Println(ca.Get(ct, "bb"))
})
}
}
func TestMapCache_Flush(t *testing.T) {
type args struct {
ct context.Context
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args
}
ca := *NewMemoryMapCacheByFn[string, string](fn, time.Second)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args{
ct,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(ca.Get(ct, "aa"))
tt.m.Flush(tt.args.ct)
fmt.Println(ca.Get(ct, "aa"))
})
}
}
func TestMapCache_Get(t *testing.T) {
type args[K comparable] struct {
ct context.Context
k K
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K]
want V
want1 bool
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{ct, "aa"},
want: "aaaa",
want1: true,
},
{
name: "t2",
m: ca,
args: args[string]{ct, "cc"},
want: "",
want1: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := tt.m.Get(tt.args.ct, tt.args.k)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("Get() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestMapCache_GetCache(t *testing.T) {
type args[K comparable] struct {
c context.Context
key K
timeout time.Duration
params []any
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K]
want V
wantErr bool
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{c: ct, key: "xx", timeout: time.Second, params: []any{ct, "xx"}},
want: "xxxx",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.m.GetCache(tt.args.c, tt.args.key, tt.args.timeout, tt.args.params...)
if (err != nil) != tt.wantErr {
t.Errorf("GetCache() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetCache() got = %v, want %v", got, tt.want)
}
})
}
}
func TestMapCache_GetCacheBatch(t *testing.T) {
type args[K comparable] struct {
c context.Context
key []K
timeout time.Duration
params []any
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K]
want []V
wantErr bool
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{
c: ct,
key: []string{"xx", "oo"},
timeout: time.Second,
params: []any{ct, []string{"xx", "oo"}},
},
want: []string{"xxxx", "oooo"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.m.GetCacheBatch(tt.args.c, tt.args.key, tt.args.timeout, tt.args.params...)
if (err != nil) != tt.wantErr {
t.Errorf("GetCacheBatch() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetCacheBatch() got = %v, want %v", got, tt.want)
}
})
}
}
func TestMapCache_GetLastSetTime(t *testing.T) {
type args[K comparable] struct {
ct context.Context
k K
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K]
wantT time.Time
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{ct, "aa"},
wantT: ttt,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotT := tt.m.GetLastSetTime(tt.args.ct, tt.args.k); !reflect.DeepEqual(gotT, tt.wantT) {
t.Errorf("GetLastSetTime() = %v, want %v", gotT, tt.wantT)
}
})
}
}
func TestMapCache_Set(t *testing.T) {
type args[K comparable, V any] struct {
ct context.Context
k K
v V
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K, V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string, string]{
ct, "xx", "yy",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(ca.Get(ct, "xx"))
tt.m.Set(tt.args.ct, tt.args.k, tt.args.v)
fmt.Println(ca.Get(ct, "xx"))
})
}
}
func TestMapCache_SetCacheBatchFn(t *testing.T) {
type args[K comparable, V any] struct {
fn func(...any) (map[K]V, error)
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K, V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string, string]{batchFn},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.m.SetCacheBatchFn(tt.args.fn)
})
}
}
func TestMapCache_SetCacheFunc(t *testing.T) {
type args[V any] struct {
fn func(...any) (V, error)
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{fn: fn},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.m.SetCacheFunc(tt.args.fn)
})
}
}
func TestMapCache_Ttl(t *testing.T) {
type args[K comparable] struct {
ct context.Context
k K
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K]
want time.Duration
}
tx := time.Now()
txx := ca.GetLastSetTime(ct, "aa")
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string]{ct, "aa"},
want: ca.expireTime - tx.Sub(txx),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Printf("过期时间=%v \nttl=%v \n当前时间 =%v\n最后设置时间=%v\n当时时间-最后设置时间=%v ", ca.expireTime, ca.Ttl(ct, "aa"), tx, txx, tx.Sub(txx))
if got := tt.m.Ttl(tt.args.ct, tt.args.k); got != tt.want {
t.Errorf("Ttl() = %v, want %v", got, tt.want)
}
})
}
}
func TestMapCache_setCacheFn(t *testing.T) {
type args[K comparable, V any] struct {
fn func(...any) (map[K]V, error)
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K, V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string, string]{batchFn},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ca.cacheFunc = nil
tt.m.setCacheFn(tt.args.fn)
fmt.Println(ca.GetCache(ct, "xx", time.Second, ct, "xx"))
})
}
}

View File

@ -2,8 +2,8 @@ package cache
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"sync"
"time" "time"
) )
@ -11,6 +11,25 @@ type MemoryMapCache[K comparable, V any] struct {
safety.Map[K, mapVal[V]] safety.Map[K, mapVal[V]]
} }
func NewMemoryMapCacheByFn[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
return &MapCache[K, V]{
data: NewMemoryMapCache[K, V](),
cacheFunc: fn,
expireTime: expireTime,
mux: sync.Mutex{},
}
}
func NewMemoryMapCacheByBatchFn[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
r := &MapCache[K, V]{
data: NewMemoryMapCache[K, V](),
batchCacheFn: fn,
expireTime: expireTime,
mux: sync.Mutex{},
}
r.setCacheFn(fn)
return r
}
func NewMemoryMapCache[K comparable, V any]() *MemoryMapCache[K, V] { func NewMemoryMapCache[K comparable, V any]() *MemoryMapCache[K, V] {
return &MemoryMapCache[K, V]{Map: safety.NewMap[K, mapVal[V]]()} return &MemoryMapCache[K, V]{Map: safety.NewMap[K, mapVal[V]]()}
} }
@ -49,9 +68,9 @@ func (m *MemoryMapCache[K, V]) Set(_ context.Context, key K, val V, _ time.Durat
func (m *MemoryMapCache[K, V]) Ttl(_ context.Context, key K, expire time.Duration) time.Duration { func (m *MemoryMapCache[K, V]) Ttl(_ context.Context, key K, expire time.Duration) time.Duration {
v, ok := m.Load(key) v, ok := m.Load(key)
if !ok { if !ok {
return -1 return time.Duration(-1)
} }
return number.Max(time.Duration(0), expire-time.Duration(time.Now().UnixNano()-v.setTime.UnixNano())) return expire - time.Now().Sub(v.setTime)
} }
func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int { func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int {

246
cache/memorymapcache_test.go vendored Normal file
View File

@ -0,0 +1,246 @@
package cache
import (
"context"
"fmt"
"reflect"
"testing"
"time"
)
var mm MemoryMapCache[string, string]
var ctx context.Context
var ttt time.Time
func init() {
ctx = context.Background()
mm = *NewMemoryMapCache[string, string]()
ttt = time.Now()
mm.Store("aa", mapVal[string]{
setTime: ttt,
ver: 1,
data: "bb",
})
time.Sleep(60 * time.Millisecond)
mm.Store("cc", mapVal[string]{
setTime: time.Now(),
ver: 1,
data: "dd",
})
}
func TestMemoryMapCache_ClearExpired(t *testing.T) {
type args struct {
in0 context.Context
expire time.Duration
}
type testCase[K string, V string] struct {
name string
m MemoryMapCache[K, V]
args args
}
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args{
in0: ctx,
expire: time.Second,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(tt.m)
tt.m.ClearExpired(tt.args.in0, tt.args.expire)
time.Sleep(time.Second)
fmt.Println(tt.m)
})
}
}
func TestMemoryMapCache_Delete(t *testing.T) {
type args[K comparable] struct {
in0 context.Context
key K
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args[K]
}
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args[string]{
in0: ctx,
key: "aa",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(mm.Get(ctx, "aa"))
tt.m.Delete(tt.args.in0, tt.args.key)
fmt.Println(mm.Get(ctx, "aa"))
})
}
}
func TestMemoryMapCache_Flush(t *testing.T) {
type args struct {
in0 context.Context
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args
}
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args{
in0: ctx,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.m.Flush(tt.args.in0)
mm.Set(ctx, "aa", "xx", time.Second)
fmt.Println(mm.Get(ctx, "aa"))
})
}
}
func TestMemoryMapCache_Get(t *testing.T) {
type args[K comparable] struct {
in0 context.Context
key K
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args[K]
wantR V
wantOk bool
}
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args[string]{
in0: ctx,
key: "aa",
},
wantOk: true,
wantR: "bb",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotR, gotOk := tt.m.Get(tt.args.in0, tt.args.key)
if !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("Get() gotR = %v, want %v", gotR, tt.wantR)
}
if gotOk != tt.wantOk {
t.Errorf("Get() gotOk = %v, want %v", gotOk, tt.wantOk)
}
})
}
}
func TestMemoryMapCache_Set(t *testing.T) {
type args[K comparable, V any] struct {
in0 context.Context
key K
val V
in3 time.Duration
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args[K, V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args[string, string]{
in0: ctx,
key: "ee",
val: "ff",
in3: time.Second,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.m.Set(tt.args.in0, tt.args.key, tt.args.val, tt.args.in3)
fmt.Println(tt.m.Get(ctx, tt.args.key))
})
}
}
func TestMemoryMapCache_Ttl(t *testing.T) {
type args[K comparable] struct {
in0 context.Context
key K
expire time.Duration
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args[K]
want time.Duration
}
tt := time.Now()
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args[string]{key: "aa", in0: ctx, expire: time.Second * 4},
want: 4*time.Second - tt.Sub(ttt),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.m.Ttl(tt.args.in0, tt.args.key, tt.args.expire); got != tt.want {
t.Errorf("Ttl() = %v, want %v", got, tt.want)
}
})
}
}
func TestMemoryMapCache_Ver(t *testing.T) {
type args[K comparable] struct {
in0 context.Context
key K
}
type testCase[K comparable, V any] struct {
name string
m MemoryMapCache[K, V]
args args[K]
want int
}
mm.Set(ctx, "aa", "ff", time.Second)
tests := []testCase[string, string]{
{
name: "t1",
m: mm,
args: args[string]{ctx, "aa"},
want: 2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.m.Ver(tt.args.in0, tt.args.key); got != tt.want {
t.Errorf("Ver() = %v, want %v", got, tt.want)
}
})
}
}