This repository has been archived by the owner on May 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathmemcached.go
132 lines (114 loc) · 3.49 KB
/
memcached.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
129
130
131
132
package cache
import (
"context"
"math"
"net"
"time"
"github.com/Shopify/go-encoding"
"github.com/bradfitz/gomemcache/memcache"
)
func NewMemcacheClient(c *memcache.Client, enc encoding.ValueEncoding) Client {
return &memcacheClient{client: c, encoding: enc}
}
type memcacheClient struct {
client *memcache.Client
encoding encoding.ValueEncoding
}
func (c *memcacheClient) Get(ctx context.Context, key string, data interface{}) error {
mItem, err := c.client.Get(key)
if err != nil {
// Abstract the memcache-specific error
if err == memcache.ErrCacheMiss {
return ErrCacheMiss
}
return coalesceTimeoutError(err)
}
return c.encoding.Decode(mItem.Value, data)
}
func (c *memcacheClient) Set(ctx context.Context, key string, data interface{}, expiration time.Time) error {
mItem, err := c.encodeItem(key, data, expiration)
if err != nil {
return err
}
return coalesceTimeoutError(c.client.Set(mItem))
}
func (c *memcacheClient) Add(ctx context.Context, key string, data interface{}, expiration time.Time) error {
mItem, err := c.encodeItem(key, data, expiration)
if err != nil {
return err
}
err = c.client.Add(mItem)
if err == memcache.ErrNotStored {
// Abstract the memcache-specific error
return ErrNotStored
}
return coalesceTimeoutError(err)
}
func (c *memcacheClient) Delete(ctx context.Context, key string) error {
err := c.client.Delete(key)
if err == memcache.ErrCacheMiss {
// Deleting a missing entry is not an actual issue.
return nil
}
return coalesceTimeoutError(err)
}
func (c *memcacheClient) Increment(ctx context.Context, key string, delta uint64) (uint64, error) {
newValue, err := c.client.Increment(key, delta)
if err == memcache.ErrCacheMiss {
// Initialize
err = c.Add(context.Background(), key, delta, NeverExpire)
if err == ErrNotStored {
// Race condition, try increment again
return c.Increment(context.Background(), key, delta)
}
newValue = delta
}
return newValue, coalesceTimeoutError(err)
}
func (c *memcacheClient) Decrement(ctx context.Context, key string, delta uint64) (uint64, error) {
newValue, err := c.client.Decrement(key, delta)
if err == memcache.ErrCacheMiss {
// Initialize
err = c.Add(context.Background(), key, -delta, NeverExpire)
if err == ErrNotStored {
// Race condition, try increment again
return c.Decrement(context.Background(), key, delta)
}
newValue = -delta
}
return newValue, coalesceTimeoutError(err)
}
func (c *memcacheClient) encodeItem(key string, data interface{}, expiration time.Time) (*memcache.Item, error) {
encoded, err := c.encoding.Encode(data)
if err != nil {
return nil, err
}
mItem := &memcache.Item{
Value: encoded,
Key: key,
}
if ttl := ttlForExpiration(expiration); ttl != 0 {
mItem.Expiration = int32(math.Max(ttl.Seconds(), 1))
}
return mItem, nil
}
type connectTimeoutError struct{}
func (connectTimeoutError) Error() string { return "memcache: connect timeout" }
func (connectTimeoutError) Timeout() bool { return true }
func (connectTimeoutError) Temporary() bool { return true }
func coalesceTimeoutError(err error) error {
// For some reason, gomemcache decided to replace the standard net.Error.
// Coalesce into a generic net.Error so that client don't have to deal with memcache-specific errors.
switch typed := err.(type) {
case *memcache.ConnectTimeoutError:
return &net.OpError{
Err: &connectTimeoutError{},
Addr: typed.Addr,
Net: typed.Addr.Network(),
Op: "connect",
}
default:
// This also work if err is nil
return err
}
}