1 package cache
2
3 import (
4 "github.com/garyburd/redigo/redis"
5 "time"
6 )
7
8
9 type RedisStore struct {
10 pool *redis.Pool
11 defaultExpiration time.Duration
12 }
13
14
15 func NewRedisCache(host string, password string, defaultExpiration time.Duration) *RedisStore {
16 var pool = &redis.Pool{
17 MaxIdle: 5,
18 IdleTimeout: 240 * time.Second,
19 Dial: func() (redis.Conn, error) {
20
21 c, err := redis.Dial("tcp", host)
22 if err != nil {
23 return nil, err
24 }
25 if len(password) > 0 {
26 if _, err := c.Do("AUTH", password); err != nil {
27 c.Close()
28 return nil, err
29 }
30 } else {
31
32 if _, err := c.Do("PING"); err != nil {
33 c.Close()
34 return nil, err
35 }
36 }
37 return c, err
38 },
39
40 TestOnBorrow: func(c redis.Conn, t time.Time) error {
41 if _, err := c.Do("PING"); err != nil {
42 return err
43 }
44 return nil
45 },
46 }
47 return &RedisStore{pool, defaultExpiration}
48 }
49
50 func (c *RedisStore) Set(key string, value interface{}, expires time.Duration) error {
51 return c.invoke(c.pool.Get().Do, key, value, expires)
52 }
53
54 func (c *RedisStore) Add(key string, value interface{}, expires time.Duration) error {
55 conn := c.pool.Get()
56 if exists(conn, key) {
57 return ErrNotStored
58 }
59 return c.invoke(conn.Do, key, value, expires)
60 }
61
62 func (c *RedisStore) Replace(key string, value interface{}, expires time.Duration) error {
63 conn := c.pool.Get()
64 if !exists(conn, key) {
65 return ErrNotStored
66 }
67 err := c.invoke(conn.Do, key, value, expires)
68 if value == nil {
69 return ErrNotStored
70 } else {
71 return err
72 }
73 }
74
75 func (c *RedisStore) Get(key string, ptrValue interface{}) error {
76 conn := c.pool.Get()
77 defer conn.Close()
78 raw, err := conn.Do("GET", key)
79 if raw == nil {
80 return ErrCacheMiss
81 }
82 item, err := redis.Bytes(raw, err)
83 if err != nil {
84 return err
85 }
86 return deserialize(item, ptrValue)
87 }
88
89 func exists(conn redis.Conn, key string) bool {
90 retval, _ := redis.Bool(conn.Do("EXISTS", key))
91 return retval
92 }
93
94 func (c *RedisStore) Delete(key string) error {
95 conn := c.pool.Get()
96 defer conn.Close()
97 if !exists(conn, key) {
98 return ErrCacheMiss
99 }
100 _, err := conn.Do("DEL", key)
101 return err
102 }
103
104 func (c *RedisStore) Increment(key string, delta uint64) (uint64, error) {
105 conn := c.pool.Get()
106 defer conn.Close()
107
108
109
110
111 val, err := conn.Do("GET", key)
112 if val == nil {
113 return 0, ErrCacheMiss
114 }
115 if err == nil {
116 currentVal, err := redis.Int64(val, nil)
117 if err != nil {
118 return 0, err
119 }
120 var sum int64 = currentVal + int64(delta)
121 _, err = conn.Do("SET", key, sum)
122 if err != nil {
123 return 0, err
124 }
125 return uint64(sum), nil
126 } else {
127 return 0, err
128 }
129 }
130
131 func (c *RedisStore) Decrement(key string, delta uint64) (newValue uint64, err error) {
132 conn := c.pool.Get()
133 defer conn.Close()
134
135
136 if !exists(conn, key) {
137 return 0, ErrCacheMiss
138 }
139
140
141
142 currentVal, err := redis.Int64(conn.Do("GET", key))
143 if err == nil && delta > uint64(currentVal) {
144 tempint, err := redis.Int64(conn.Do("DECRBY", key, currentVal))
145 return uint64(tempint), err
146 }
147 tempint, err := redis.Int64(conn.Do("DECRBY", key, delta))
148 return uint64(tempint), err
149 }
150
151 func (c *RedisStore) Flush() error {
152 conn := c.pool.Get()
153 defer conn.Close()
154 _, err := conn.Do("FLUSHALL")
155 return err
156 }
157
158 func (c *RedisStore) invoke(f func(string, ...interface{}) (interface{}, error),
159 key string, value interface{}, expires time.Duration) error {
160
161 switch expires {
162 case DEFAULT:
163 expires = c.defaultExpiration
164 case FOREVER:
165 expires = time.Duration(0)
166 }
167
168 b, err := serialize(value)
169 if err != nil {
170 return err
171 }
172 conn := c.pool.Get()
173 defer conn.Close()
174 if expires > 0 {
175 _, err := f("SETEX", key, int32(expires/time.Second), b)
176 return err
177 } else {
178 _, err := f("SET", key, b)
179 return err
180 }
181 }
182
View as plain text