-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient_set.go
136 lines (112 loc) · 3.4 KB
/
client_set.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
133
134
135
136
package mc
import (
"strconv"
"github.com/kinescope/mc/proto/cache"
)
func (c *Client) Add(i *Item, o ...MsOption) error {
return c.populateOne("E", i, 0, o...)
}
func (c *Client) Set(i *Item, o ...MsOption) error {
return c.populateOne("S", i, 0, o...)
}
func (c *Client) CompareAndSwap(i *Item, o ...MsOption) error {
return c.populateOne("S", i, i.cas, o...)
}
/*
- b: interpret key as base64 encoded binary value (see metaget)
- c: return CAS value if successfully stored.
- C(token): compare CAS value when storing item
- E(token): use token as new CAS value (see metaget for detail)
- F(token): set client flags to token (32 bit unsigned numeric)
- I: invalidate. set-to-invalid if supplied CAS is older than item's CAS
- k: return key as a token
- O(token): opaque value, consumes a token and copies back with response
- q: use noreply semantics for return codes
- s: return the size of the stored item on success (ie; new size on append)
- T(token): Time-To-Live for item, see "Expiration" above.
- M(token): mode switch to change behavior to add, replace, append, prepend
- N(token): if in append mode, autovivify on miss with supplied TTL
E: "add" command. LRU bump and return NS if item exists. Else
add.
A: "append" command. If item exists, append the new value to its data.
P: "prepend" command. If item exists, prepend the new value to its data.
R: "replace" command. Set only if item already exists.
S: "set" command. The default mode, added for completeness.
*/
// https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L685
func (c *Client) populateOne(mode string, i *Item, cas uint64, o ...MsOption) (retErr error) {
if len(i.Value) == 0 {
return ErrEmptyValue
}
var opts msOpts
for _, fn := range o {
fn(&opts)
}
if opts.minUses != 0 {
if v, err := c.Inc(i.Key+"::_min_uses", 1, opts.expiration, WithInitialValue(1)); err == nil && v < opts.minUses {
return nil
}
}
key, err := c.encodeKey(i.Key)
if err != nil {
return err
}
conn, err := c.pickServer(key)
if err != nil {
return err
}
defer c.pool.condRelease(conn, retErr)
var (
flags int
source [4]byte
)
if len(opts.namespace) != 0 {
flags |= serialized
ver, err := c.nsVersion(opts.namespace, 0)
if err != nil {
return err
}
i.Value, err = (&cache.Item{
Data: i.Value,
Namespace: &cache.Namespace{
Key: opts.namespace,
Ver: ver,
},
}).Marshal()
if err != nil {
return err
}
}
if opts.compressionMinLen != 0 && len(i.Value) > opts.compressionMinLen {
flags |= compressed
if i.Value, err = c.compress(i.Value); err != nil {
return err
}
}
cmd := []byte("ms " + key + " ")
cmd = strconv.AppendInt(cmd, int64(len(i.Value)), 10)
cmd = append(append(cmd, ' ', 'M'), []byte(mode)...)
if opts.expiration != 0 {
cmd = append(cmd, ' ', 'T')
cmd = strconv.AppendUint(cmd, uint64(opts.expiration), 10)
}
if i.Flags != 0 || flags != 0 {
endian.PutUint16(source[:2], uint16(flags))
endian.PutUint16(source[2:], uint16(i.Flags))
cmd = append(cmd, ' ', 'F')
cmd = strconv.AppendUint(cmd, uint64(endian.Uint32(source[:])), 10)
}
if !c.opts.DisableBinaryEncodedKeys {
cmd = append(cmd, ' ', 'b')
}
if cas != 0 {
cmd = append(cmd, ' ', 'C')
cmd = strconv.AppendUint(cmd, cas, 10)
}
conn.buff.Write(append(cmd, crlf...))
conn.buff.Write(append(i.Value, crlf...))
if err := conn.buff.Flush(); err != nil {
return err
}
return parseResponse(conn.buff.Reader)
}