diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 372dd48b..7a14b012 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -137,12 +137,12 @@ func (rc *Cache) Decr(key string) error { // ClearAll clean all cache in redis. delete this redis collection. func (rc *Cache) ClearAll() error { - c := rc.p.Get() - defer c.Close() - cachedKeys, err := redis.Strings(c.Do("KEYS", rc.key+":*")) + cachedKeys, err := rc.Scan(rc.key + ":*") if err != nil { return err } + c := rc.p.Get() + defer c.Close() for _, str := range cachedKeys { if _, err = c.Do("DEL", str); err != nil { return err @@ -151,6 +151,35 @@ func (rc *Cache) ClearAll() error { return err } +// Scan scan all keys matching the pattern. a better choice than `keys` +func (rc *Cache) Scan(pattern string) (keys []string, err error) { + c := rc.p.Get() + defer c.Close() + var ( + cursor uint64 = 0 // start + result []interface{} + list []string + ) + for { + result, err = redis.Values(c.Do("SCAN", cursor, "MATCH", pattern, "COUNT", 1024)) + if err != nil { + return + } + list, err = redis.Strings(result[1], nil) + if err != nil { + return + } + keys = append(keys, list...) + cursor, err = redis.Uint64(result[0], nil) + if err != nil { + return + } + if cursor == 0 { // over + return + } + } +} + // StartAndGC start redis cache adapter. // config is like {"key":"collection key","conn":"connection info","dbNum":"0"} // the cache item in redis are stored forever, diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go index 56877f6b..7ac88f87 100644 --- a/cache/redis/redis_test.go +++ b/cache/redis/redis_test.go @@ -15,6 +15,7 @@ package redis import ( + "fmt" "testing" "time" @@ -104,3 +105,40 @@ func TestRedisCache(t *testing.T) { t.Error("clear all err") } } + +func TestCache_Scan(t *testing.T) { + timeoutDuration := 10 * time.Second + // init + bm, err := cache.NewCache("redis", `{"conn": "127.0.0.1:6379"}`) + if err != nil { + t.Error("init err") + } + // insert all + for i := 0; i < 10000; i++ { + if err = bm.Put(fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration); err != nil { + t.Error("set Error", err) + } + } + // scan all for the first time + keys, err := bm.(*Cache).Scan(DefaultKey + ":*") + if err != nil { + t.Error("scan Error", err) + } + if len(keys) != 10000 { + t.Error("scan all err") + } + + // clear all + if err = bm.ClearAll(); err != nil { + t.Error("clear all err") + } + + // scan all for the second time + keys, err = bm.(*Cache).Scan(DefaultKey + ":*") + if err != nil { + t.Error("scan Error", err) + } + if len(keys) != 0 { + t.Error("scan all err") + } +}