diff --git a/cache/cache.go b/cache/cache.go index ddb2f857..7ca87802 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -47,6 +47,8 @@ import ( type Cache interface { // get cached value by key. Get(key string) interface{} + // GetMulti is a batch version of Get. + GetMulti(keys []string) []interface{} // set cached value with key and expire time. Put(key string, val interface{}, timeout int64) error // delete cached value by key. diff --git a/cache/cache_test.go b/cache/cache_test.go index 7c43e539..481309fd 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -65,6 +65,35 @@ func TestCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + + //test GetMulti + if err = bm.Put("astaxie", "author", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + if v := bm.Get("astaxie"); v.(string) != "author" { + t.Error("get err") + } + + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } } func TestFileCache(t *testing.T) { @@ -102,6 +131,7 @@ func TestFileCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + //test string if err = bm.Put("astaxie", "author", 10); err != nil { t.Error("set Error", err) @@ -109,9 +139,28 @@ func TestFileCache(t *testing.T) { if !bm.IsExist("astaxie") { t.Error("check err") } - if v := bm.Get("astaxie"); v.(string) != "author" { t.Error("get err") } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } + os.RemoveAll("cache") } diff --git a/cache/file.go b/cache/file.go index bbbbbad2..65f114f3 100644 --- a/cache/file.go +++ b/cache/file.go @@ -132,6 +132,16 @@ func (fc *FileCache) Get(key string) interface{} { return to.Data } +// GetMulti gets values from file cache. +// if non-exist or expired, return empty string. +func (fc *FileCache) GetMulti(keys []string) []interface{} { + var rc []interface{} + for _, key := range keys { + rc = append(rc, fc.Get(key)) + } + return rc +} + // Put value into file cache. // timeout means how long to keep this file, unit of ms. // if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. diff --git a/cache/memcache/memcache.go b/cache/memcache/memcache.go index f5a5c6ef..c6829054 100644 --- a/cache/memcache/memcache.go +++ b/cache/memcache/memcache.go @@ -63,6 +63,32 @@ func (rc *MemcacheCache) Get(key string) interface{} { return nil } +// get value from memcache. +func (rc *MemcacheCache) GetMulti(keys []string) []interface{} { + size := len(keys) + var rv []interface{} + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + for i := 0; i < size; i++ { + rv = append(rv, err) + } + return rv + } + } + mv, err := rc.conn.GetMulti(keys) + if err == nil { + for _, v := range mv { + rv = append(rv, string(v.Value)) + } + return rv + } else { + for i := 0; i < size; i++ { + rv = append(rv, err) + } + return rv + } +} + // put value to memcache. only support string. func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { if rc.conn == nil { diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go new file mode 100644 index 00000000..0523ae85 --- /dev/null +++ b/cache/memcache/memcache_test.go @@ -0,0 +1,106 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package memcache + +import ( + _ "github.com/bradfitz/gomemcache/memcache" + + "github.com/astaxie/beego/cache" + "strconv" + "testing" + "time" +) + +func TestRedisCache(t *testing.T) { + bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`) + if err != nil { + t.Error("init err") + } + if err = bm.Put("astaxie", "1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + + time.Sleep(10 * time.Second) + + if bm.IsExist("astaxie") { + t.Error("check err") + } + if err = bm.Put("astaxie", "1", 10); err != nil { + t.Error("set Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + t.Error("get err") + } + + if err = bm.Incr("astaxie"); err != nil { + t.Error("Incr Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 { + t.Error("get err") + } + + if err = bm.Decr("astaxie"); err != nil { + t.Error("Decr Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + t.Error("get err") + } + bm.Delete("astaxie") + if bm.IsExist("astaxie") { + t.Error("delete err") + } + + //test string + if err = bm.Put("astaxie", "author", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + + if v := bm.Get("astaxie").(string); v != "author" { + t.Error("get err") + } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } + + // test clear all + if err = bm.ClearAll(); err != nil { + t.Error("clear all err") + } +} diff --git a/cache/memory.go b/cache/memory.go index 0233be7d..b6657048 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -64,6 +64,16 @@ func (bc *MemoryCache) Get(name string) interface{} { return nil } +// GetMulti gets caches from memory. +// if non-existed or expired, return nil. +func (bc *MemoryCache) GetMulti(names []string) []interface{} { + var rc []interface{} + for _, name := range names { + rc = append(rc, bc.Get(name)) + } + return rc +} + // Put cache to memory. // if expired is 0, it will be cleaned by next gc operation ( default gc clock is 1 minute). func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error { diff --git a/cache/redis/redis.go b/cache/redis/redis.go index ba543f61..d14b1ada 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -75,6 +75,39 @@ func (rc *RedisCache) Get(key string) interface{} { return nil } +// GetMulti get cache from redis. +func (rc *RedisCache) GetMulti(keys []string) []interface{} { + size := len(keys) + var rv []interface{} + c := rc.p.Get() + defer c.Close() + var err error + for _, key := range keys { + err = c.Send("GET", key) + if err != nil { + goto ERROR + } + } + if err = c.Flush(); err != nil { + goto ERROR + } + for i := 0; i < size; i++ { + if v, err := c.Receive(); err == nil { + rv = append(rv, v.([]byte)) + } else { + rv = append(rv, err) + } + } + return rv +ERROR: + rv = rv[0:0] + for i := 0; i < size; i++ { + rv = append(rv, nil) + } + + return rv +} + // put cache to redis. func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { var err error diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go index fbe82ac5..1f74fd27 100644 --- a/cache/redis/redis_test.go +++ b/cache/redis/redis_test.go @@ -67,6 +67,7 @@ func TestRedisCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + //test string if err = bm.Put("astaxie", "author", 10); err != nil { t.Error("set Error", err) @@ -78,6 +79,26 @@ func TestRedisCache(t *testing.T) { if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" { t.Error("get err") } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if v, _ := redis.String(vv[0], nil); v != "author" { + t.Error("GetMulti ERROR") + } + if v, _ := redis.String(vv[1], nil); v != "author1" { + t.Error("GetMulti ERROR") + } + // test clear all if err = bm.ClearAll(); err != nil { t.Error("clear all err")