diff --git a/cache/cache.go b/cache/cache.go index 82585c4e..fd805ab9 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -59,6 +59,10 @@ type Cache interface { Incr(key string) error // decrease cached int value by key, as a counter. Decr(key string) error + // increase cached with int value by key, as a counter. + IncrBy(key string, num int) error + // decrease cached with int value by key, as a counter. + DecrBy(key string, num int) error // check if cached value exists or not. IsExist(key string) bool // clear all cache. diff --git a/cache/cache_test.go b/cache/cache_test.go index 9ceb606a..5e6546e2 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -37,7 +37,7 @@ func TestCache(t *testing.T) { t.Error("get err") } - time.Sleep(30 * time.Second) + time.Sleep(10 * time.Second) if bm.IsExist("astaxie") { t.Error("check err") @@ -55,6 +55,22 @@ func TestCache(t *testing.T) { t.Error("get err") } + if err = bm.IncrBy("astaxie", 2); err != nil { + t.Error("Incr Error", err) + } + + if v := bm.Get("astaxie"); v.(int) != 4 { + t.Error("get err") + } + + if err = bm.DecrBy("astaxie", 2); err != nil { + t.Error("Decr Error", err) + } + + if v := bm.Get("astaxie"); v.(int) != 2 { + t.Error("get err") + } + if err = bm.Decr("astaxie"); err != nil { t.Error("Decr Error", err) } @@ -62,6 +78,7 @@ func TestCache(t *testing.T) { if v := bm.Get("astaxie"); v.(int) != 1 { t.Error("get err") } + bm.Delete("astaxie") if bm.IsExist("astaxie") { t.Error("delete err") @@ -122,6 +139,22 @@ func TestFileCache(t *testing.T) { t.Error("get err") } + if err = bm.IncrBy("astaxie", 2); err != nil { + t.Error("Incr Error", err) + } + + if v := bm.Get("astaxie"); v.(int) != 4 { + t.Error("get err") + } + + if err = bm.DecrBy("astaxie", 2); err != nil { + t.Error("Decr Error", err) + } + + if v := bm.Get("astaxie"); v.(int) != 2 { + t.Error("get err") + } + if err = bm.Decr("astaxie"); err != nil { t.Error("Decr Error", err) } @@ -129,6 +162,7 @@ func TestFileCache(t *testing.T) { if v := bm.Get("astaxie"); v.(int) != 1 { t.Error("get err") } + bm.Delete("astaxie") if bm.IsExist("astaxie") { t.Error("delete err") diff --git a/cache/file.go b/cache/file.go index 691ce7cd..99d92212 100644 --- a/cache/file.go +++ b/cache/file.go @@ -20,6 +20,7 @@ import ( "encoding/gob" "encoding/hex" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -183,6 +184,24 @@ func (fc *FileCache) Incr(key string) error { return nil } +// IncrBy will increase cached int value by num. +// fc value is saving forever unless Delete. +func (fc *FileCache) IncrBy(key string, num int) error { + if num < 1 { + return errors.New("increase num should be a positive number") + } + + data := fc.Get(key) + var incr int + if reflect.TypeOf(data).Name() != "int" { + incr = 0 + } else { + incr = data.(int) + int(num) + } + fc.Put(key, incr, FileCacheEmbedExpiry) + return nil +} + // Decr will decrease cached int value. func (fc *FileCache) Decr(key string) error { data := fc.Get(key) @@ -196,6 +215,23 @@ func (fc *FileCache) Decr(key string) error { return nil } +// DecrBy will decrease cached int value. +func (fc *FileCache) DecrBy(key string, num int) error { + if num < 1 { + return errors.New("decrease num should be a positive number") + } + + data := fc.Get(key) + var decr int + if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { + decr = 0 + } else { + decr = data.(int) - int(num) + } + fc.Put(key, decr, FileCacheEmbedExpiry) + return nil +} + // IsExist check value is exist. func (fc *FileCache) IsExist(key string) bool { ret, _ := exists(fc.getCacheFileName(key)) diff --git a/cache/memcache/memcache.go b/cache/memcache/memcache.go index 0624f5fa..97822f1c 100644 --- a/cache/memcache/memcache.go +++ b/cache/memcache/memcache.go @@ -127,6 +127,21 @@ func (rc *Cache) Incr(key string) error { return err } +// IncrBy increase counter by num. +func (rc *Cache) IncrBy(key string, num int) error { + if num < 1 { + return errors.New("increase num should be a positive number") + } + + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + return err + } + } + _, err := rc.conn.Increment(key, uint64(num)) + return err +} + // Decr decrease counter. func (rc *Cache) Decr(key string) error { if rc.conn == nil { @@ -138,6 +153,21 @@ func (rc *Cache) Decr(key string) error { return err } +// DecrBy decrease counter by num. +func (rc *Cache) DecrBy(key string, num int) error { + if num < 1 { + return errors.New("decrease num should be a positive number") + } + + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + return err + } + } + _, err := rc.conn.Decrement(key, uint64(num)) + return err +} + // IsExist check value exists in memcache. func (rc *Cache) IsExist(key string) bool { if rc.conn == nil { diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go index d9129b69..0a6ee934 100644 --- a/cache/memcache/memcache_test.go +++ b/cache/memcache/memcache_test.go @@ -58,6 +58,22 @@ func TestMemcacheCache(t *testing.T) { t.Error("get err") } + if err = bm.IncrBy("astaxie", 2); err != nil { + t.Error("Incr Error", err) + } + + if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 4 { + t.Error("get err") + } + + if err = bm.DecrBy("astaxie", 2); err != nil { + t.Error("Decr Error", err) + } + + if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 2 { + t.Error("get err") + } + if err = bm.Decr("astaxie"); err != nil { t.Error("Decr Error", err) } @@ -65,6 +81,7 @@ func TestMemcacheCache(t *testing.T) { if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 { t.Error("get err") } + bm.Delete("astaxie") if bm.IsExist("astaxie") { t.Error("delete err") diff --git a/cache/memory.go b/cache/memory.go index 57e868cf..1b894a81 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -135,6 +135,39 @@ func (bc *MemoryCache) Incr(key string) error { return nil } +// IncrBy increase cache counter in memory by num. +// it supports int,int32,int64,uint,uint32,uint64. +func (bc *MemoryCache) IncrBy(key string, num int) error { + bc.RLock() + defer bc.RUnlock() + itm, ok := bc.items[key] + if !ok { + return errors.New("key not exist") + } + + if num < 1 { + return errors.New("increase num should be a positive number") + } + + switch itm.val.(type) { + case int: + itm.val = itm.val.(int) + num + case int32: + itm.val = itm.val.(int32) + int32(num) + case int64: + itm.val = itm.val.(int64) + int64(num) + case uint: + itm.val = itm.val.(uint) + uint(num) + case uint32: + itm.val = itm.val.(uint32) + uint32(num) + case uint64: + itm.val = itm.val.(uint64) + uint64(num) + default: + return errors.New("item val is not (u)int (u)int32 (u)int64") + } + return nil +} + // Decr decrease counter in memory. func (bc *MemoryCache) Decr(key string) error { bc.RLock() @@ -174,6 +207,50 @@ func (bc *MemoryCache) Decr(key string) error { return nil } +// DecrBy decrease counter in memory by num. +func (bc *MemoryCache) DecrBy(key string, num int) error { + bc.RLock() + defer bc.RUnlock() + itm, ok := bc.items[key] + if !ok { + return errors.New("key not exist") + } + + if num < 1 { + return errors.New("decrease num should be a positive number") + } + + switch itm.val.(type) { + case int: + itm.val = itm.val.(int) - int(num) + case int64: + itm.val = itm.val.(int64) - int64(num) + case int32: + itm.val = itm.val.(int32) - int32(num) + case uint: + if itm.val.(uint) > 0 { + itm.val = itm.val.(uint) - uint(num) + } else { + return errors.New("item val is less than 0") + } + case uint32: + if itm.val.(uint32) > 0 { + itm.val = itm.val.(uint32) - uint32(num) + } else { + return errors.New("item val is less than 0") + } + case uint64: + if itm.val.(uint64) > 0 { + itm.val = itm.val.(uint64) - uint64(num) + } else { + return errors.New("item val is less than 0") + } + default: + return errors.New("item val is not int int64 int32") + } + return nil +} + // IsExist check cache exist in memory. func (bc *MemoryCache) IsExist(name string) bool { bc.RLock() diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 55fea25e..72af1d68 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -128,12 +128,30 @@ func (rc *Cache) Incr(key string) error { return err } +// IncrBy increase counter in redis by num. +func (rc *Cache) IncrBy(key string, num int) error { + if num < 1 { + return errors.New("increase num should be a positive number") + } + _, err := redis.Bool(rc.do("INCRBY", key, num)) + return err +} + // Decr decrease counter in redis. func (rc *Cache) Decr(key string) error { _, err := redis.Bool(rc.do("INCRBY", key, -1)) return err } +// DecrBy decrease counter in redis by num. +func (rc *Cache) DecrBy(key string, num int) error { + if num < 1 { + return errors.New("decrease num should be a positive number") + } + _, err := redis.Bool(rc.do("DECRBY", key, num)) + return err +} + // ClearAll clean all cache in redis. delete this redis collection. func (rc *Cache) ClearAll() error { c := rc.p.Get() diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go index 56877f6b..3979014b 100644 --- a/cache/redis/redis_test.go +++ b/cache/redis/redis_test.go @@ -56,6 +56,22 @@ func TestRedisCache(t *testing.T) { t.Error("get err") } + if err = bm.IncrBy("astaxie", 2); err != nil { + t.Error("Incr Error", err) + } + + if v, _ := redis.Int(bm.Get("astaxie"), err); v != 4 { + t.Error("get err") + } + + if err = bm.DecrBy("astaxie", 2); err != nil { + t.Error("Decr Error", err) + } + + if v, _ := redis.Int(bm.Get("astaxie"), err); v != 2 { + t.Error("get err") + } + if err = bm.Decr("astaxie"); err != nil { t.Error("Decr Error", err) } @@ -63,6 +79,7 @@ func TestRedisCache(t *testing.T) { if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 { t.Error("get err") } + bm.Delete("astaxie") if bm.IsExist("astaxie") { t.Error("delete err") diff --git a/cache/ssdb/ssdb.go b/cache/ssdb/ssdb.go index fa2ce04b..7b57e45d 100644 --- a/cache/ssdb/ssdb.go +++ b/cache/ssdb/ssdb.go @@ -124,6 +124,20 @@ func (rc *Cache) Incr(key string) error { return err } +// IncrBy increase counter by num. +func (rc *Cache) IncrBy(key string, num int) error { + if num < 1 { + return errors.New("increase num should be a positive number") + } + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + return err + } + } + _, err := rc.conn.Do("incr", key, num) + return err +} + // Decr decrease counter. func (rc *Cache) Decr(key string) error { if rc.conn == nil { @@ -135,6 +149,21 @@ func (rc *Cache) Decr(key string) error { return err } +// DecrBy decrease counter by num. +func (rc *Cache) DecrBy(key string, num int) error { + if num < 1 { + return errors.New("decrease num should be a positive number") + } + + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + return err + } + } + _, err := rc.conn.Do("incr", key, -num) + return err +} + // IsExist check value exists in memcache. func (rc *Cache) IsExist(key string) bool { if rc.conn == nil { diff --git a/cache/ssdb/ssdb_test.go b/cache/ssdb/ssdb_test.go index dd474960..96102a56 100644 --- a/cache/ssdb/ssdb_test.go +++ b/cache/ssdb/ssdb_test.go @@ -40,6 +40,7 @@ func TestSsdbcacheCache(t *testing.T) { if err = ssdb.Put("ssdb", "2", timeoutDuration); err != nil { t.Error("set Error", err) } + if err = ssdb.Incr("ssdb"); err != nil { t.Error("incr Error", err) } @@ -48,10 +49,30 @@ func TestSsdbcacheCache(t *testing.T) { t.Error("get err") } + if err = ssdb.IncrBy("ssdb", 2); err != nil { + t.Error("incr Error", err) + } + + if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 5 { + t.Error("get err") + } + + if err = ssdb.DecrBy("ssdb", 2); err != nil { + t.Error("decr error") + } + + if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 { + t.Error("get err") + } + if err = ssdb.Decr("ssdb"); err != nil { t.Error("decr error") } + if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 2 { + t.Error("get err") + } + // test del if err = ssdb.Put("ssdb", "3", timeoutDuration); err != nil { t.Error("set Error", err)