/
redis.go
128 lines (105 loc) · 2.85 KB
/
redis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package store
import (
"fmt"
"strings"
"time"
"github.com/go-redis/redis/v7"
)
// RedisClientInterface represents a go-redis/redis client
type RedisClientInterface interface {
Get(key string) *redis.StringCmd
Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd
Del(keys ...string) *redis.IntCmd
}
const (
// RedisType represents the storage type as a string value
RedisType = "redis"
// RedisTagPattern represents the tag pattern to be used as a key in specified storage
RedisTagPattern = "gocache_tag_%s"
)
// RedisStore is a store for Redis
type RedisStore struct {
client RedisClientInterface
options *Options
}
// NewRedis creates a new store to Redis instance(s)
func NewRedis(client RedisClientInterface, options *Options) *RedisStore {
if options == nil {
options = &Options{}
}
return &RedisStore{
client: client,
options: options,
}
}
// Get returns data stored from a given key
func (s *RedisStore) Get(key interface{}) (interface{}, error) {
return s.client.Get(key.(string)).Result()
}
// Set defines data in Redis for given key identifier
func (s *RedisStore) Set(key interface{}, value interface{}, options *Options) error {
if options == nil {
options = s.options
}
err := s.client.Set(key.(string), value, options.ExpirationValue()).Err()
if err != nil {
return err
}
if tags := options.TagsValue(); len(tags) > 0 {
s.setTags(key, tags)
}
return nil
}
func (s *RedisStore) setTags(key interface{}, tags []string) {
for _, tag := range tags {
var tagKey = fmt.Sprintf(RedisTagPattern, tag)
var cacheKeys = []string{}
if result, err := s.Get(tagKey); err == nil {
if bytes, ok := result.([]byte); ok {
cacheKeys = strings.Split(string(bytes), ",")
}
}
var alreadyInserted = false
for _, cacheKey := range cacheKeys {
if cacheKey == key.(string) {
alreadyInserted = true
break
}
}
if !alreadyInserted {
cacheKeys = append(cacheKeys, key.(string))
}
s.Set(tagKey, []byte(strings.Join(cacheKeys, ",")), &Options{
Expiration: 720 * time.Hour,
})
}
}
// Delete removes data from Redis for given key identifier
func (s *RedisStore) Delete(key interface{}) error {
_, err := s.client.Del(key.(string)).Result()
return err
}
// Invalidate invalidates some cache data in Redis for given options
func (s *RedisStore) Invalidate(options InvalidateOptions) error {
if tags := options.TagsValue(); len(tags) > 0 {
for _, tag := range tags {
var tagKey = fmt.Sprintf(RedisTagPattern, tag)
result, err := s.Get(tagKey)
if err != nil {
return nil
}
var cacheKeys = []string{}
if bytes, ok := result.([]byte); ok {
cacheKeys = strings.Split(string(bytes), ",")
}
for _, cacheKey := range cacheKeys {
s.Delete(cacheKey)
}
}
}
return nil
}
// GetType returns the store type
func (s *RedisStore) GetType() string {
return RedisType
}