diff --git a/example/main.go b/example/main.go index 7bbe6da..ab84c35 100644 --- a/example/main.go +++ b/example/main.go @@ -36,6 +36,7 @@ func main() { glg.Info(v3) } + glg.Debugf("Len:\t%d", gache.Len()) // set gache default expire time gc := gache.New().SetDefaultExpire(time.Second * 10) diff --git a/gache.go b/gache.go index 6e65a0b..1e31f3e 100644 --- a/gache.go +++ b/gache.go @@ -32,6 +32,7 @@ type ( SetExpiredHook(f func(context.Context, string)) Gache SetWithExpire(string, interface{}, time.Duration) StartExpired(context.Context, time.Duration) Gache + Len() int ToMap(context.Context) *sync.Map ToRawMap(context.Context) map[string]interface{} Write(context.Context, io.Writer) error @@ -63,6 +64,7 @@ type ( expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } @@ -192,7 +194,7 @@ func ToMap(ctx context.Context) *sync.Map { // ToRawMap returns All Cache Key-Value map func (g *gache) ToRawMap(ctx context.Context) map[string]interface{} { - m := make(map[string]interface{}) + m := make(map[string]interface{}, g.Len()) mu := new(sync.Mutex) g.Foreach(ctx, func(key string, val interface{}, exp int64) bool { mu.Lock() @@ -251,6 +253,7 @@ func (g *gache) set(key string, val interface{}, expire int64) { if expire > 0 { expire = fastime.UnixNanoNow() + expire } + atomic.AddUint64(&g.l, 1) g.shards[xxhash.Sum64(*(*[]byte)(unsafe.Pointer(&key)))&0xFF].Store(key, value{ expire: expire, val: val, @@ -279,6 +282,7 @@ func Set(key string, val interface{}) { // Delete deletes value from Gache using key func (g *gache) Delete(key string) { + atomic.StoreUint64(&g.l, atomic.LoadUint64(&g.l)-1) g.shards[xxhash.Sum64(*(*[]byte)(unsafe.Pointer(&key)))&0xFF].Delete(key) } @@ -358,9 +362,20 @@ func Foreach(ctx context.Context, f func(string, interface{}, int64) bool) Gache return instance.Foreach(ctx, f) } +// Len returns stored object length +func Len() int { + return instance.Len() +} + +// Len returns stored object length +func (g *gache) Len() int { + l := atomic.LoadUint64(&g.l) + return *(*int)(unsafe.Pointer(&l)) +} + // Write writes all cached data to writer func (g *gache) Write(ctx context.Context, w io.Writer) error { - m := make(map[string]value) + m := make(map[string]value, g.Len()) mu := new(sync.Mutex) gb := gob.NewEncoder(lz4.NewWriter(w)) g.Foreach(ctx, func(key string, val interface{}, exp int64) bool { diff --git a/gache_test.go b/gache_test.go index 61495a5..37b5893 100644 --- a/gache_test.go +++ b/gache_test.go @@ -92,6 +92,7 @@ func Test_gache_SetDefaultExpire(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -113,6 +114,7 @@ func Test_gache_SetDefaultExpire(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.SetDefaultExpire(tt.args.ex); !reflect.DeepEqual(got, tt.want) { @@ -149,6 +151,7 @@ func Test_gache_EnableExpiredHook(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } tests := []struct { @@ -166,6 +169,7 @@ func Test_gache_EnableExpiredHook(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.EnableExpiredHook(); !reflect.DeepEqual(got, tt.want) { @@ -198,6 +202,7 @@ func Test_gache_DisableExpiredHook(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } tests := []struct { @@ -215,6 +220,7 @@ func Test_gache_DisableExpiredHook(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.DisableExpiredHook(); !reflect.DeepEqual(got, tt.want) { @@ -247,6 +253,7 @@ func Test_gache_SetExpiredHook(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -268,6 +275,7 @@ func Test_gache_SetExpiredHook(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.SetExpiredHook(tt.args.f); !reflect.DeepEqual(got, tt.want) { @@ -304,6 +312,7 @@ func Test_gache_StartExpired(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -326,6 +335,7 @@ func Test_gache_StartExpired(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.StartExpired(tt.args.ctx, tt.args.dur); !reflect.DeepEqual(got, tt.want) { @@ -342,6 +352,7 @@ func Test_gache_ToMap(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -363,6 +374,7 @@ func Test_gache_ToMap(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.ToMap(tt.args.ctx); !reflect.DeepEqual(got, tt.want) { @@ -399,6 +411,7 @@ func Test_gache_ToRawMap(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -420,6 +433,7 @@ func Test_gache_ToRawMap(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.ToRawMap(tt.args.ctx); !reflect.DeepEqual(got, tt.want) { @@ -456,6 +470,7 @@ func Test_gache_get(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -479,6 +494,7 @@ func Test_gache_get(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } got, got1, got2 := g.get(tt.args.key) @@ -502,6 +518,7 @@ func Test_gache_Get(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -524,6 +541,7 @@ func Test_gache_Get(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } got, got1 := g.Get(tt.args.key) @@ -569,6 +587,7 @@ func Test_gache_GetWithExpire(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -592,6 +611,7 @@ func Test_gache_GetWithExpire(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } got, got1, got2 := g.GetWithExpire(tt.args.key) @@ -644,6 +664,7 @@ func Test_gache_set(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -666,6 +687,7 @@ func Test_gache_set(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.set(tt.args.key, tt.args.val, tt.args.expire) @@ -680,6 +702,7 @@ func Test_gache_SetWithExpire(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -702,6 +725,7 @@ func Test_gache_SetWithExpire(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.SetWithExpire(tt.args.key, tt.args.val, tt.args.expire) @@ -735,6 +759,7 @@ func Test_gache_Set(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -756,6 +781,7 @@ func Test_gache_Set(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.Set(tt.args.key, tt.args.val) @@ -788,6 +814,7 @@ func Test_gache_Delete(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -808,6 +835,7 @@ func Test_gache_Delete(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.Delete(tt.args.key) @@ -839,6 +867,7 @@ func Test_gache_expiration(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -859,6 +888,7 @@ func Test_gache_expiration(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.expiration(tt.args.key) @@ -873,6 +903,7 @@ func Test_gache_DeleteExpired(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -894,6 +925,7 @@ func Test_gache_DeleteExpired(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.DeleteExpired(tt.args.ctx); got != tt.want { @@ -930,6 +962,7 @@ func Test_gache_Foreach(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -952,6 +985,7 @@ func Test_gache_Foreach(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if got := g.Foreach(tt.args.ctx, tt.args.f); !reflect.DeepEqual(got, tt.want) { @@ -982,6 +1016,57 @@ func TestForeach(t *testing.T) { } } +func TestLen(t *testing.T) { + tests := []struct { + name string + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Len(); got != tt.want { + t.Errorf("Len() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_gache_Len(t *testing.T) { + type fields struct { + expChan chan string + expFunc func(context.Context, string) + expFuncEnabled bool + expGroup singleflight.Group + expire int64 + l uint64 + shards [256]*sync.Map + } + tests := []struct { + name string + fields fields + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := &gache{ + expChan: tt.fields.expChan, + expFunc: tt.fields.expFunc, + expFuncEnabled: tt.fields.expFuncEnabled, + expGroup: tt.fields.expGroup, + expire: tt.fields.expire, + l: tt.fields.l, + shards: tt.fields.shards, + } + if got := g.Len(); got != tt.want { + t.Errorf("gache.Len() = %v, want %v", got, tt.want) + } + }) + } +} + func Test_gache_Write(t *testing.T) { type fields struct { expChan chan string @@ -989,6 +1074,7 @@ func Test_gache_Write(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -1011,6 +1097,7 @@ func Test_gache_Write(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } w := &bytes.Buffer{} @@ -1058,6 +1145,7 @@ func Test_gache_Read(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } type args struct { @@ -1079,6 +1167,7 @@ func Test_gache_Read(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } if err := g.Read(tt.args.r); (err != nil) != tt.wantErr { @@ -1115,6 +1204,7 @@ func Test_gache_Clear(t *testing.T) { expFuncEnabled bool expGroup singleflight.Group expire int64 + l uint64 shards [256]*sync.Map } tests := []struct { @@ -1131,6 +1221,7 @@ func Test_gache_Clear(t *testing.T) { expFuncEnabled: tt.fields.expFuncEnabled, expGroup: tt.fields.expGroup, expire: tt.fields.expire, + l: tt.fields.l, shards: tt.fields.shards, } g.Clear() diff --git a/go.mod b/go.mod index 4e24562..86629dc 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/kpango/gache +go 1.12 + require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20181116193636-154af6d8a4dc github.com/allegro/bigcache v1.2.0 @@ -11,6 +13,6 @@ require ( github.com/kpango/glg v1.2.10 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pierrec/lz4 v2.0.5+incompatible - golang.org/x/sync v0.0.0-20190227205108-e225da77a7e6 + golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 google.golang.org/appengine v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index ccd9f41..08627c6 100644 --- a/go.sum +++ b/go.sum @@ -6,16 +6,15 @@ github.com/allegro/bigcache v1.1.0/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc github.com/allegro/bigcache v1.2.0 h1:qDaE0QoF29wKBb3+pXFrJFy1ihe5OT9OiXhg1t85SxM= github.com/allegro/bigcache v1.2.0/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= -github.com/bluele/gcache v0.0.0-20190203144525-2016d595ccb0 h1:vUdUwmQLnT/yuk8PsDhhMVkrfr4aMdcv/0GWzIqOjEY= -github.com/bluele/gcache v0.0.0-20190203144525-2016d595ccb0/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= -github.com/bluele/gcache v0.0.0-20190301055842-79ae3b2d8680/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= +github.com/bluele/gcache v0.0.0-20190301044115-79ae3b2d8680 h1:jk2k2FUNIg9ogv9yFIzjA5NTrJeaAkHev4AFspbmyvo= +github.com/bluele/gcache v0.0.0-20190301044115-79ae3b2d8680/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= github.com/bouk/monkey v1.0.1/go.mod h1:PG/63f4XEUlVyW1ttIeOJmJhhe1+t9EC/je3eTjvFhE= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.0.0 h1:Eb1IiuHmi3FhT12NKfqCQXSXRqc4NTMvgJoREemrSt4= github.com/cespare/xxhash/v2 v2.0.0/go.mod h1:MaMeaVDXZNmTpkOyhVs3/WfjgobkbQgfrVnrr3DyZL0= -github.com/coocood/freecache v1.0.1 h1:oFyo4msX2c0QIKU+kuMJUwsKamJ+AKc2JJrKcMszJ5M= github.com/coocood/freecache v1.0.1/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= +github.com/coocood/freecache v1.1.0 h1:ENiHOsWdj1BrrlPwblhbn4GdAsMymK3pZORJ+bJGAjA= github.com/coocood/freecache v1.1.0/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -26,8 +25,7 @@ github.com/kpango/fastime v1.0.0/go.mod h1:Y5XY5bLG5yc7g2XmMUzc22XYV1XaH+KgUOHkD github.com/kpango/fastime v1.0.8 h1:Wif5eocdsIXmMG+8HHfRP/jD6UUl+/OVTJ+sMzvA1+E= github.com/kpango/fastime v1.0.8/go.mod h1:Y5XY5bLG5yc7g2XmMUzc22XYV1XaH+KgUOHkDvLp4SA= github.com/kpango/gache v1.1.0/go.mod h1:BHKRCYnJ2pRFFIJNc61KTJb3KXSzlrt/ITfgfCQJAJw= -github.com/kpango/glg v1.2.9 h1:gP6lEaXwsOAg1YCpfy1KRu60bH2lzaD7yU7RMwV4SQw= -github.com/kpango/glg v1.2.9/go.mod h1:VK6nyghBgjAig+nIlROLPDC8fiUQFvgfzgyX0jeUY4o= +github.com/kpango/glg v1.2.10 h1:/Zp8nzbWlONIucnYbHvqNC2ZTzyibAB/S9nE/zfdLtI= github.com/kpango/glg v1.2.10/go.mod h1:VK6nyghBgjAig+nIlROLPDC8fiUQFvgfzgyX0jeUY4o= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -39,10 +37,8 @@ github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227205108-e225da77a7e6 h1:4g1lye0KHuJTfefZzSW4G2TJQ0cqIclHqEb2sQymjGo= -golang.org/x/sync v0.0.0-20190227205108-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=