Skip to content

Commit 6f6c610

Browse files
committed
feat: cache support tag
1 parent 72099f2 commit 6f6c610

File tree

7 files changed

+349
-14
lines changed

7 files changed

+349
-14
lines changed

cache/memory_store.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type item struct {
1515

1616
// Expired Returns true if the item has expired.
1717
func (item item) Expired() bool {
18-
if item.Expiration == 0 {
18+
if item.Expiration < 0 {
1919
return false
2020
}
2121
return time.Now().UnixNano() > item.Expiration
@@ -65,8 +65,8 @@ func (s *MemoryStore) Get(key string, val interface{}) error {
6565

6666
// Put set cached value with key and expire time.
6767
func (s *MemoryStore) Put(key string, val interface{}, timeout time.Duration) error {
68-
var e int64
69-
if timeout > 0 {
68+
var e int64 = -1
69+
if timeout >= 0 {
7070
e = time.Now().Add(timeout).UnixNano()
7171
}
7272

@@ -78,7 +78,7 @@ func (s *MemoryStore) Put(key string, val interface{}, timeout time.Duration) er
7878
Expiration: e,
7979
}
8080

81-
if e > 0 {
81+
if e >= 0 {
8282
s.DeleteExpired()
8383
}
8484

@@ -133,6 +133,11 @@ func (s *MemoryStore) Decrement(key string, value ...int) (int, error) {
133133
return by, nil
134134
}
135135

136+
// Forever Store an item in the cache indefinitely.
137+
func (s *MemoryStore) Forever(key string, val interface{}) error {
138+
return s.Put(key, val, 0)
139+
}
140+
136141
// Exist check cache's existence in memory.
137142
func (s *MemoryStore) Exist(key string) bool {
138143
s.mu.RLock()
@@ -149,8 +154,8 @@ func (s *MemoryStore) Exist(key string) bool {
149154

150155
// Expire set value expire time.
151156
func (s *MemoryStore) Expire(key string, timeout time.Duration) error {
152-
var e int64
153-
if timeout > 0 {
157+
var e int64 = -1
158+
if timeout >= 0 {
154159
e = time.Now().Add(timeout).UnixNano()
155160
}
156161

@@ -165,7 +170,7 @@ func (s *MemoryStore) Expire(key string, timeout time.Duration) error {
165170
item.Expiration = e
166171
s.items[s.prefix+key] = item
167172

168-
if e > 0 {
173+
if e >= 0 {
169174
s.DeleteExpired()
170175
}
171176

@@ -188,6 +193,11 @@ func (s *MemoryStore) Flush() error {
188193
return nil
189194
}
190195

196+
func (s *MemoryStore) Tags(names ...string) Store {
197+
// tags not be supported
198+
return s
199+
}
200+
191201
func (s *MemoryStore) TTL(key string) (int64, error) {
192202
s.mu.RLock()
193203
defer s.mu.RUnlock()
@@ -230,7 +240,7 @@ func (s *MemoryStore) DeleteExpired() {
230240

231241
smallestDuration := 0 * time.Nanosecond
232242
for key, item := range s.items {
233-
if item.Expiration == 0 {
243+
if item.Expiration < 0 {
234244
continue
235245
}
236246
// "Inlining" of expired

cache/redis_store.go

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@ package cache
33
import (
44
"encoding/json"
55
"fmt"
6+
"strings"
67
"time"
78

89
"github.com/gomodule/redigo/redis"
910
)
1011

12+
// ReferenceKeyForever Forever reference key.
13+
const ReferenceKeyForever = "forever_ref"
14+
15+
// ReferenceKeyStandard Standard reference key.
16+
const ReferenceKeyStandard = "standard_ref"
17+
1118
type RedisStore struct {
1219
pool *redis.Pool // redis connection pool
20+
tagSet *TagSet
1321
prefix string
1422
}
1523

16-
// NewStore Create a redis cache store
24+
// NewRedisStore Create a redis cache store
1725
func NewRedisStore(pool *redis.Pool, prefix string) *RedisStore {
1826
s := RedisStore{}
1927
return s.SetPool(pool).SetPrefix(prefix)
@@ -38,6 +46,12 @@ func (s *RedisStore) Put(key string, val interface{}, timeout time.Duration) err
3846
if err != nil {
3947
return err
4048
}
49+
50+
err = s.pushStandardKeys(key)
51+
if err != nil {
52+
return err
53+
}
54+
4155
c := s.pool.Get()
4256
defer c.Close()
4357
_, err = c.Do("SETEX", s.prefix+key, int64(timeout/time.Second), string(b))
@@ -46,6 +60,11 @@ func (s *RedisStore) Put(key string, val interface{}, timeout time.Duration) err
4660

4761
// Increment the value of an item in the cache.
4862
func (s *RedisStore) Increment(key string, value ...int) (int, error) {
63+
err := s.pushStandardKeys(key)
64+
if err != nil {
65+
return 0, err
66+
}
67+
4968
c := s.pool.Get()
5069
defer c.Close()
5170

@@ -59,6 +78,11 @@ func (s *RedisStore) Increment(key string, value ...int) (int, error) {
5978

6079
// Decrement the value of an item in the cache.
6180
func (s *RedisStore) Decrement(key string, value ...int) (int, error) {
81+
err := s.pushStandardKeys(key)
82+
if err != nil {
83+
return 0, err
84+
}
85+
6286
c := s.pool.Get()
6387
defer c.Close()
6488

@@ -70,6 +94,24 @@ func (s *RedisStore) Decrement(key string, value ...int) (int, error) {
7094
return redis.Int(c.Do("DECRBY", s.prefix+key, by))
7195
}
7296

97+
// Forever Store an item in the cache indefinitely.
98+
func (s *RedisStore) Forever(key string, val interface{}) error {
99+
b, err := json.Marshal(val)
100+
if err != nil {
101+
return err
102+
}
103+
104+
err = s.pushForeverKeys(key)
105+
if err != nil {
106+
return err
107+
}
108+
109+
c := s.pool.Get()
110+
defer c.Close()
111+
_, err = c.Do("SET", s.prefix+key, string(b))
112+
return err
113+
}
114+
73115
// Exist check cache's existence in redis.
74116
func (s *RedisStore) Exist(key string) bool {
75117
c := s.pool.Get()
@@ -100,6 +142,26 @@ func (s *RedisStore) Forget(key string) error {
100142

101143
// Remove all items from the cache.
102144
func (s *RedisStore) Flush() error {
145+
if s.tagSet != nil {
146+
err := s.deleteForeverKeys()
147+
if err != nil {
148+
return err
149+
}
150+
err = s.deleteStandardKeys()
151+
if err != nil {
152+
return err
153+
}
154+
err = s.tagSet.Reset()
155+
if err != nil {
156+
return err
157+
}
158+
return nil
159+
}
160+
161+
return s.flush()
162+
}
163+
164+
func (s *RedisStore) flush() error {
103165
c := s.pool.Get()
104166
defer c.Close()
105167

@@ -121,12 +183,34 @@ func (s *RedisStore) Flush() error {
121183
break
122184
}
123185
}
124-
for _, key := range keys {
125-
if _, err = c.Do("DEL", key); err != nil {
126-
return err
186+
187+
length := len(keys)
188+
if length == 0 {
189+
return nil
190+
}
191+
192+
var keysChunk []interface{}
193+
for i, key := range keys {
194+
keysChunk = append(keysChunk, key)
195+
if i == length-1 || len(keysChunk) == 1000 {
196+
_, err = c.Do("DEL", keysChunk...)
197+
if err != nil {
198+
return err
199+
}
127200
}
128201
}
129-
return err
202+
203+
return nil
204+
}
205+
206+
func (s *RedisStore) Tags(names ...string) Store {
207+
if len(names) == 0 {
208+
return s
209+
}
210+
ss := s.clone()
211+
ss.tagSet = NewTagSet(s, names)
212+
213+
return ss
130214
}
131215

132216
func (s *RedisStore) TTL(key string) (int64, error) {
@@ -156,3 +240,108 @@ func (s *RedisStore) SetPrefix(prefix string) *RedisStore {
156240
}
157241
return s
158242
}
243+
244+
func (s *RedisStore) clone() *RedisStore {
245+
return &RedisStore{
246+
pool: s.pool,
247+
prefix: s.prefix,
248+
}
249+
}
250+
251+
func (s *RedisStore) pushStandardKeys(key string) error {
252+
return s.pushKeys(key, ReferenceKeyStandard)
253+
}
254+
255+
func (s *RedisStore) pushForeverKeys(key string) error {
256+
return s.pushKeys(key, ReferenceKeyForever)
257+
}
258+
259+
func (s *RedisStore) pushKeys(key, reference string) error {
260+
if s.tagSet == nil {
261+
return nil
262+
}
263+
264+
namespace, err := s.tagSet.GetNamespace()
265+
if err != nil {
266+
return err
267+
}
268+
269+
fullKey := s.prefix + key
270+
segments := strings.Split(namespace, "|")
271+
272+
c := s.pool.Get()
273+
defer c.Close()
274+
for _, segment := range segments {
275+
_, err = c.Do("SADD", s.referenceKey(segment, reference), fullKey)
276+
if err != nil {
277+
return err
278+
}
279+
}
280+
return nil
281+
}
282+
283+
func (s *RedisStore) deleteStandardKeys() error {
284+
return s.deleteKeysByReference(ReferenceKeyStandard)
285+
}
286+
287+
func (s *RedisStore) deleteForeverKeys() error {
288+
return s.deleteKeysByReference(ReferenceKeyForever)
289+
}
290+
291+
func (s *RedisStore) deleteKeysByReference(reference string) error {
292+
if s.tagSet == nil {
293+
return nil
294+
}
295+
296+
namespace, err := s.tagSet.GetNamespace()
297+
if err != nil {
298+
return err
299+
}
300+
segments := strings.Split(namespace, "|")
301+
c := s.pool.Get()
302+
defer c.Close()
303+
304+
for _, segment := range segments {
305+
segment = s.referenceKey(segment, reference)
306+
err = s.deleteKeys(segment)
307+
if err != nil {
308+
return err
309+
}
310+
_, err = c.Do("DEL", segment)
311+
if err != nil {
312+
return err
313+
}
314+
}
315+
316+
return nil
317+
}
318+
319+
func (s *RedisStore) deleteKeys(referenceKey string) error {
320+
c := s.pool.Get()
321+
defer c.Close()
322+
keys, err := redis.Strings(c.Do("SMEMBERS", referenceKey))
323+
if err != nil {
324+
return err
325+
}
326+
var length = len(keys)
327+
if length == 0 {
328+
return nil
329+
}
330+
331+
var keysChunk []interface{}
332+
for i, key := range keys {
333+
keysChunk = append(keysChunk, key)
334+
if i == length-1 || len(keysChunk) == 1000 {
335+
_, err = c.Do("DEL", keysChunk...)
336+
if err != nil {
337+
return err
338+
}
339+
}
340+
}
341+
342+
return nil
343+
}
344+
345+
func (s *RedisStore) referenceKey(segment, suffix string) string {
346+
return s.prefix + segment + ":" + suffix
347+
}

0 commit comments

Comments
 (0)