add more manage methods. modify some cache logic

This commit is contained in:
inhere
2021-06-30 01:24:32 +08:00
parent e7800134ac
commit 1af54fad12
21 changed files with 343 additions and 115 deletions

View File

@@ -5,12 +5,10 @@ import (
"time"
"github.com/dgraph-io/badger"
"github.com/gookit/cache"
)
// BadgerDB definition
type BadgerDB struct {
cache.BaseDriver
db *badger.DB
}

View File

@@ -43,7 +43,7 @@ func (c *BoltDB) Get(key string) interface{} {
b := tx.Bucket([]byte(c.Bucket))
bs := b.Get([]byte(key))
if err := c.MustUnmarshal(bs, &val); err != nil {
if err := c.UnmarshalTo(bs, &val); err != nil {
return err
}
@@ -53,7 +53,6 @@ func (c *BoltDB) Get(key string) interface{} {
if err != nil {
return nil
}
return val
}

View File

@@ -72,7 +72,7 @@ func (c *BuntDB) Get(key string) interface{} {
return err
}
return c.MustUnmarshal([]byte(str), &val)
return c.UnmarshalTo([]byte(str), &val)
})
if err != nil {
@@ -119,7 +119,7 @@ func (c *BuntDB) GetMulti(keys []string) map[string]interface{} {
}
var val interface{}
err = c.MustUnmarshal([]byte(str), &val)
err = c.UnmarshalTo([]byte(str), &val)
if err != nil {
return err
}

View File

@@ -83,11 +83,20 @@ var std = NewManager()
// Register driver to manager instance
func Register(name string, driver Cache) *Manager {
std.DefaultUse(name)
std.Register(name, driver)
return std
}
// Unregister an cache driver
func Unregister(name string) {
std.Unregister(name)
}
// UnregisterAll cache drivers
func UnregisterAll(fn ...func(cache Cache)) {
std.UnregisterAll(fn...)
}
// SetDefName set default driver name.
// Deprecated
// please use DefaultUse() instead it

View File

@@ -90,8 +90,8 @@ func (l *BaseDriver) Marshal(val interface{}) (interface{}, error) {
return val, nil
}
// MustUnmarshal cache value
func (l *BaseDriver) MustUnmarshal(bts []byte, ptr interface{}) error {
// UnmarshalTo cache value
func (l *BaseDriver) UnmarshalTo(bts []byte, ptr interface{}) error {
if Unmarshal == nil {
return errNoUnmarshal
}
@@ -115,16 +115,6 @@ func (l *BaseDriver) Unmarshal(val []byte, err error) interface{} {
return val
}
// GetAs get cache value and decode value to object ptr
func (l *BaseDriver) GetAs(key string, ptr interface{}) error {
// TODO bts, err := c.Get(key)
// if Unmarshal != nil {
// err := Unmarshal(bts, ptr)
// return err
// }
panic("please implement me")
}
// Key real cache key build
func (l *BaseDriver) Key(key string) string {
if l.opt.Prefix != "" {

View File

@@ -12,6 +12,7 @@ import (
// FileCache definition.
type FileCache struct {
BaseDriver
// caches in memory
MemoryCache
// cache directory path
@@ -81,7 +82,7 @@ func (c *FileCache) get(key string) interface{} {
}
item := &Item{}
if err = c.MustUnmarshal(bs, item); err != nil {
if err = c.UnmarshalTo(bs, item); err != nil {
c.SetLastErr(err)
return nil
}

View File

@@ -7,7 +7,6 @@ import (
// MemoryCache definition.
type MemoryCache struct {
BaseDriver
// locker
lock sync.RWMutex
// cache data in memory. or use sync.Map
@@ -55,8 +54,8 @@ func (c *MemoryCache) Get(key string) interface{} {
func (c *MemoryCache) get(key string) interface{} {
if item, ok := c.caches[key]; ok {
// check expire time
// if has been expired, remove it.
if item.Invalid() {
// if has been expired, remove it.
_ = c.del(key)
}
@@ -103,37 +102,37 @@ func (c *MemoryCache) del(key string) error {
// GetMulti values by multi key
func (c *MemoryCache) GetMulti(keys []string) map[string]interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
values := make(map[string]interface{}, len(keys))
data := make(map[string]interface{}, len(keys))
for _, key := range keys {
values[key] = c.get(key)
data[key] = c.get(key)
}
return values
c.lock.RUnlock()
return data
}
// SetMulti values by multi key
func (c *MemoryCache) SetMulti(values map[string]interface{}, ttl time.Duration) (err error) {
c.lock.Lock()
defer c.lock.Unlock()
for key, val := range values {
if err = c.set(key, val, ttl); err != nil {
return
}
}
c.lock.Unlock()
return
}
// DelMulti values by multi key
func (c *MemoryCache) DelMulti(keys []string) error {
c.lock.Lock()
defer c.lock.Unlock()
for _, key := range keys {
_ = c.del(key)
}
c.lock.Unlock()
return nil
}

View File

@@ -1,13 +1,12 @@
package cache_test
import (
"math/rand"
"strconv"
"testing"
"time"
"github.com/gookit/cache"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/strutil"
"github.com/stretchr/testify/assert"
)
@@ -38,7 +37,6 @@ type user struct {
func TestMemoryCache_object(t *testing.T) {
is := assert.New(t)
b1 := user {
Age: 1,
Name: "inhere",
@@ -46,8 +44,9 @@ func TestMemoryCache_object(t *testing.T) {
c := cache.NewMemoryCache()
key := randomKey()
t.Log("cache key:", key)
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + key)
is.False(c.Has(key))
err := c.Set(key, b1, cache.Seconds3)
@@ -110,8 +109,8 @@ func TestFileCache_object(t *testing.T) {
Name: "inhere",
}
key := randomKey()
dump.P("cache key:", c.Key(key))
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + c.Key(key))
err := c.Set(key, b1, cache.Seconds3)
is.NoError(err)
@@ -123,8 +122,3 @@ func TestFileCache_object(t *testing.T) {
// val2 := c.GetAs()
// dump.P("cache get:", val)
}
func randomKey() string {
rand.Seed(time.Now().UnixNano())
return "k" + time.Now().Format("20060102") + strconv.Itoa(rand.Intn(999))
}

View File

@@ -2,19 +2,78 @@
package gcache
import (
"time"
"github.com/bluele/gcache"
"github.com/gookit/cache"
)
// GCache driver definition
type GCache struct {
cache.BaseDriver
// cache.BaseDriver
db gcache.Cache
}
// New create an instance
func New(size int) *GCache {
return NewWithType(size, gcache.TYPE_LRU)
}
// NewWithType create an instance with cache type
func NewWithType(size int, tp string) *GCache {
return &GCache{
db: gcache.New(size).LRU().Build(),
db: gcache.New(size).EvictType(tp).Build(),
}
}
// Close connection
func (g *GCache) Close() error {
return nil
}
// Clear all caches
func (g *GCache) Clear() error {
g.db.Purge()
return nil
}
// Has cache key
func (g *GCache) Has(key string) bool {
return g.Get(key) != nil
}
// Get cache by key
func (g *GCache) Get(key string) interface{} {
val, _ := g.db.Get(key)
return val
}
// Set cache by key
func (g *GCache) Set(key string, val interface{}, ttl time.Duration) (err error) {
return g.db.SetWithExpire(key, val, ttl)
}
// Del cache by key
func (g GCache) Del(key string) error {
g.db.Remove(key)
return nil
}
// GetMulti cache by keys
func (g *GCache) GetMulti(keys []string) map[string]interface{} {
panic("implement me")
}
// SetMulti cache by keys
func (g GCache) SetMulti(values map[string]interface{}, ttl time.Duration) (err error) {
panic("implement me")
}
// DelMulti cache by keys
func (g *GCache) DelMulti(keys []string) error {
panic("implement me")
}
// Db get the gcache.Cache
func (g *GCache) Db() gcache.Cache {
return g.db
}

2
go.mod
View File

@@ -12,7 +12,7 @@ require (
github.com/golang/snappy v0.0.1 // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/gookit/goutil v0.3.14
github.com/gookit/gsr v0.0.3
github.com/gookit/gsr v0.0.4
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.7.0

2
go.sum
View File

@@ -73,6 +73,8 @@ github.com/gookit/goutil v0.3.14 h1:ZEdZR+Vkvcjz0SSC0MpjtD+Kwlg/uagpiddh6L2ko+0=
github.com/gookit/goutil v0.3.14/go.mod h1:YdGV0ObqRUlRq4/RzAQBHcd1Wzl/jKw7cppDBtD3q+U=
github.com/gookit/gsr v0.0.3 h1:aoVIw50rSPn7TuTfY6tNJ0iysnGKKnArfRdeXQaSeME=
github.com/gookit/gsr v0.0.3/go.mod h1:oCrmIEBod7DNBBFP5sckUY2yrOmJgNpG3xyxryLyJKY=
github.com/gookit/gsr v0.0.4 h1:wxwbcwN0iSovauty6IIR2Qi7fM5Ck1NrrDKE1wU9EQ8=
github.com/gookit/gsr v0.0.4/go.mod h1:oCrmIEBod7DNBBFP5sckUY2yrOmJgNpG3xyxryLyJKY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=

View File

@@ -1,34 +1,48 @@
// Package gocache is a memory cache driver implement.
// base on the package: github.com/patrickmn/go-cache
//
// Usage:
// import "github.com/gookit/cache"
//
// cache.Register(gocache.NewGoCache(0, cache.FiveMinutes))
// // use
// // cache.Set("key", "value")
package gocache
import (
"time"
"github.com/gookit/cache"
goc "github.com/patrickmn/go-cache"
)
// Name driver name
const Name = "gocache"
// GoCache struct
type GoCache struct {
cache.BaseDriver
cache *goc.Cache
// expire handle
db *goc.Cache
// will handle expire on has,get
expireManually bool
}
// New create instance
func New() *GoCache {
c := goc.New(goc.NoExpiration, goc.NoExpiration)
return NewSimple()
}
// NewSimple create new simple instance
func NewSimple() *GoCache {
return &GoCache{
cache: c,
db: goc.New(goc.NoExpiration, goc.NoExpiration),
// handle expire on has,get
expireManually: true,
}
}
// NewGoCache create instance with settings
func NewGoCache(defaultExpiration, cleanupInterval time.Duration) *GoCache {
c := goc.New(defaultExpiration, cleanupInterval)
return &GoCache{
cache: c,
db: goc.New(defaultExpiration, cleanupInterval),
}
}
@@ -39,7 +53,7 @@ func (g *GoCache) Close() error {
// Clear all caches
func (g *GoCache) Clear() error {
g.cache.Flush()
g.db.Flush()
return nil
}
@@ -51,32 +65,56 @@ func (g *GoCache) Has(key string) bool {
// Get cache by key
func (g *GoCache) Get(key string) interface{} {
if g.expireManually {
g.cache.DeleteExpired()
g.db.DeleteExpired()
}
val, _ := g.cache.Get(key)
val, _ := g.db.Get(key)
return val
}
// Set cache by key
func (g *GoCache) Set(key string, val interface{}, ttl time.Duration) (err error) {
g.cache.Set(key, val, ttl)
func (g *GoCache) Set(key string, val interface{}, ttl time.Duration) error {
g.db.Set(key, val, ttl)
return nil
}
// Del cache by key
func (g GoCache) Del(key string) error {
g.cache.Delete(key)
g.db.Delete(key)
return nil
}
// GetMulti cache by keys
func (g *GoCache) GetMulti(keys []string) map[string]interface{} {
panic("implement me")
data := make(map[string]interface{}, len(keys))
for _, key := range keys {
val, ok := g.db.Get(key)
if ok {
data[key] = val
}
}
return data
}
func (g GoCache) SetMulti(values map[string]interface{}, ttl time.Duration) (err error) {
panic("implement me")
// SetMulti cache by keys
func (g GoCache) SetMulti(values map[string]interface{}, ttl time.Duration) error {
for key, val := range values {
g.db.Set(key, val, ttl)
}
return nil
}
// DelMulti db by keys
func (g *GoCache) DelMulti(keys []string) error {
panic("implement me")
for _, key := range keys {
g.db.Delete(key)
}
return nil
}
// Db get the goc.Cache
func (g *GoCache) Db() *goc.Cache {
return g.db
}

View File

@@ -1,16 +1,23 @@
package gocache
package gocache_test
import (
"fmt"
"testing"
"time"
"github.com/gookit/cache"
"github.com/gookit/cache/gocache"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/strutil"
"github.com/stretchr/testify/assert"
)
func ExampleGoCache() {
c := New()
c := gocache.New()
key := "name"
// set
c.Set(key, "cache value", 2*time.Second)
c.Set(key, "cache value", cache.Seconds2)
fmt.Println(c.Has(key))
// get
@@ -33,3 +40,84 @@ func ExampleGoCache() {
// <nil>
// false
}
func ExampleNewGoCache_cachePkg() {
c1 := gocache.NewGoCache(cache.OneDay, cache.FiveMinutes)
cache.Register(gocache.Name, c1)
defer cache.UnregisterAll()
key := "name1"
// set
cache.Set(key, "cache value", cache.Seconds2)
fmt.Println(cache.Has(key))
// get
val := cache.Get(key)
fmt.Println(val)
time.Sleep(2 * time.Second)
// get expired
val2 := cache.Get(key)
fmt.Println(val2)
// del
cache.Del(key)
fmt.Println(cache.Has(key))
// Output:
// true
// cache value
// <nil>
// false
}
func TestGoCache_usage(t *testing.T) {
is := assert.New(t)
c := gocache.NewSimple()
defer c.Clear()
key := strutil.RandomCharsV2(12)
is.False(c.Has(key))
err := c.Set(key, "value", cache.Seconds3)
is.NoError(err)
is.True(c.Has(key))
val := c.Get(key)
is.Equal("value", val)
// del
err = c.Del(key)
is.NoError(err)
is.False(c.Has(key))
}
type user struct {
Age int
Name string
}
func TestGoCache_object(t *testing.T) {
is := assert.New(t)
c := gocache.NewSimple()
defer c.Clear()
b1 := user {
Age: 1,
Name: "inhere",
}
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + key)
is.False(c.Has(key))
err := c.Set(key, b1, cache.Seconds3)
is.NoError(err)
is.True(c.Has(key))
b2 := c.Get(key).(user)
dump.P(b2)
is.Equal("inhere", b2.Name)
}

View File

@@ -96,6 +96,16 @@ func (c *GoRedis) Get(key string) interface{} {
return c.Unmarshal(bts, err)
}
// GetAs get cache and unmarshal to ptr
func (c *GoRedis) GetAs(key string, ptr interface{}) error {
bts, err := c.rdb.Get(CtxForExec, c.Key(key)).Bytes()
if err != nil {
return err
}
return c.UnmarshalTo(bts, ptr)
}
// Set cache by key
func (c *GoRedis) Set(key string, val interface{}, ttl time.Duration) (err error) {
val, err = c.Marshal(val)

View File

@@ -2,14 +2,12 @@ package goredis_test
import (
"fmt"
"math/rand"
"strconv"
"testing"
"time"
"github.com/gookit/cache"
"github.com/gookit/cache/goredis"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/strutil"
"github.com/stretchr/testify/assert"
)
@@ -46,8 +44,9 @@ func getC() *goredis.GoRedis {
func TestGoRedis_basic(t *testing.T) {
c := getC()
key := randomKey()
t.Log("cache key", key)
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + c.Key(key))
assert.False(t, c.Has(key))
err := c.Set(key, "value", cache.Seconds3)
@@ -69,32 +68,33 @@ type user struct {
func TestRedigo_object(t *testing.T) {
c := getC()
b1 := user {
u1 := user {
Age: 12,
Name: "inhere",
}
key := randomKey()
t.Log("cache key", c.Key(key))
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + c.Key(key))
assert.False(t, c.Has(key))
err := c.Set(key, b1, cache.Seconds3)
err := c.Set(key, u1, cache.Seconds3)
assert.NoError(t, err)
assert.True(t, c.Has(key))
v := c.Get(key)
assert.NotEmpty(t, v)
// dump.P(v.(string))
dump.P(v)
u2 := user{}
err = c.GetAs(key, &u2)
assert.NoError(t, err)
dump.P(u2)
assert.Equal(t, "inhere", u2.Name)
err = c.Del(key)
assert.NoError(t, err)
assert.False(t, c.Has(key))
assert.Empty(t, c.Get(key))
}
func randomKey() string {
return time.Now().Format("20060102") + strconv.Itoa(rand.Intn(999))
}

View File

@@ -4,13 +4,12 @@ package leveldb
import (
"time"
"github.com/gookit/cache"
"github.com/syndtr/goleveldb/leveldb"
)
// LevelDB definition
type LevelDB struct {
cache.BaseDriver
// cache.BaseDriver
db *leveldb.DB
}

View File

@@ -29,7 +29,26 @@ type Manager struct {
func NewManager() *Manager {
return &Manager{
// defName: driverName,
drivers: make(map[string]Cache),
drivers: make(map[string]Cache, 8),
}
}
// Register new cache driver
func (m *Manager) Register(name string, driver Cache) *Manager {
// always use latest as default driver.
m.defName = name
// save driver instance
m.drivers[name] = driver
return m
}
// Unregister an cache driver
func (m *Manager) Unregister(name string) {
delete(m.drivers, name)
// reset default driver name.
if m.defName == name {
m.defName = ""
}
}
@@ -37,23 +56,25 @@ func NewManager() *Manager {
// Deprecated
// please use DefaultUse() instead it
func (m *Manager) SetDefName(driverName string) {
m.defName = driverName
m.DefaultUse(driverName)
}
// DefaultUse set default driver name
func (m *Manager) DefaultUse(driverName string) {
m.defName = driverName
}
if _, ok := m.drivers[driverName]; !ok {
panic("cache driver: " + driverName + " is not registered")
}
// Register new driver object
func (m *Manager) Register(name string, driver Cache) *Manager {
m.drivers[name] = driver
return m
m.defName = driverName
}
// Default returns the default driver instance
func (m *Manager) Default() Cache {
return m.drivers[m.defName]
if c, ok := m.drivers[m.defName]; ok {
return c
}
panic("cache driver: " + m.defName + " is not registered")
}
// Use driver object by name and set it as default driver.
@@ -62,7 +83,7 @@ func (m *Manager) Use(driverName string) Cache {
return m.Driver(driverName)
}
// Cache driver object by name. alias of Driver()
// Cache get driver by name. alias of Driver()
func (m *Manager) Cache(driverName string) Cache {
return m.drivers[driverName]
}
@@ -85,6 +106,19 @@ func (m *Manager) Close() (err error) {
return err
}
// UnregisterAll cache drivers
func (m *Manager) UnregisterAll(fn ...func(cache Cache)) {
m.defName = ""
for name, driver := range m.drivers {
if len(fn) > 0 {
fn[0](driver)
}
delete(m.drivers, name)
}
}
/*************************************************************
* Quick use by default cache driver
*************************************************************/

View File

@@ -50,7 +50,7 @@ func (c *MemCached) Get(key string) (val interface{}) {
return
}
err = c.MustUnmarshal(item.Value, &val)
err = c.UnmarshalTo(item.Value, &val)
if err != nil {
return nil
}
@@ -89,7 +89,7 @@ func (c *MemCached) GetMulti(keys []string) map[string]interface{} {
values := make(map[string]interface{}, len(keys))
for key, item := range items {
var val interface{}
if err := c.MustUnmarshal(item.Value, &val); err != nil {
if err := c.UnmarshalTo(item.Value, &val); err != nil {
continue
}

View File

@@ -3,13 +3,11 @@ package nutsdb
import (
"time"
"github.com/gookit/cache"
)
// NutsDB definition
// NutsDB definition TODO
type NutsDB struct {
cache.BaseDriver
// cache.BaseDriver
}
func (c *NutsDB) Has(key string) bool {

View File

@@ -56,9 +56,19 @@ func (c *Redigo) Connect() *Redigo {
// Get value by key
func (c *Redigo) Get(key string) interface{} {
val, err := redis.Bytes(c.exec("Get", c.Key(key)))
bts, err := redis.Bytes(c.exec("Get", c.Key(key)))
return c.Unmarshal(val, err)
return c.Unmarshal(bts, err)
}
// GetAs get cache and unmarshal to ptr
func (c *Redigo) GetAs(key string, ptr interface{}) error {
bts, err := redis.Bytes(c.exec("Get", c.Key(key)))
if err != nil {
return err
}
return c.UnmarshalTo(bts, ptr)
}
// Set value by key

View File

@@ -2,14 +2,12 @@ package redis_test
import (
"fmt"
"math/rand"
"strconv"
"testing"
"time"
"github.com/gookit/cache"
"github.com/gookit/cache/redis"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/strutil"
"github.com/stretchr/testify/assert"
)
@@ -45,8 +43,9 @@ func getC() *redis.Redigo {
func TestRedigo_basic(t *testing.T) {
c := getC()
key := randomKey()
t.Log("cache key", c.Key(key))
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + c.Key(key))
assert.False(t, c.Has(key))
err := c.Set(key, "value", cache.Seconds3)
@@ -72,8 +71,9 @@ func TestRedigo_object(t *testing.T) {
Name: "inhere",
}
key := randomKey()
t.Log("cache key", c.Key(key))
key := strutil.RandomCharsV2(12)
dump.P("cache key: " + c.Key(key))
assert.False(t, c.Has(key))
err := c.Set(key, b1, cache.Seconds3)
@@ -83,16 +83,16 @@ func TestRedigo_object(t *testing.T) {
v := c.Get(key)
assert.NotEmpty(t, v)
// dump.P(v.(string))
dump.P(v)
u2 := user{}
err = c.GetAs(key, &u2)
assert.NoError(t, err)
dump.P(u2)
assert.Equal(t, "inhere", u2.Name)
err = c.Del(key)
assert.NoError(t, err)
assert.False(t, c.Has(key))
assert.Empty(t, c.Get(key))
}
func randomKey() string {
return "k" + time.Now().Format("20060102") + strconv.Itoa(rand.Intn(999))
}