OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "sync" | 8 "sync" |
9 "time" | 9 "time" |
10 | 10 |
11 "golang.org/x/net/context" | 11 "golang.org/x/net/context" |
12 | 12 |
13 » "github.com/luci/gae" | 13 » "github.com/luci/gae/impl/dummy" |
14 » "github.com/luci/gae/dummy" | 14 » mc "github.com/luci/gae/service/memcache" |
15 | |
16 "github.com/luci/luci-go/common/clock" | 15 "github.com/luci/luci-go/common/clock" |
17 ) | 16 ) |
18 | 17 |
19 type mcItem struct { | 18 type mcItem struct { |
20 key string | 19 key string |
21 value []byte | 20 value []byte |
22 object interface{} | 21 object interface{} |
23 flags uint32 | 22 flags uint32 |
24 expiration time.Duration | 23 expiration time.Duration |
25 | 24 |
26 CasID uint64 | 25 CasID uint64 |
27 } | 26 } |
28 | 27 |
29 var _ gae.MCItem = (*mcItem)(nil) | 28 var _ mc.Item = (*mcItem)(nil) |
30 | 29 |
31 func (m *mcItem) Key() string { return m.key } | 30 func (m *mcItem) Key() string { return m.key } |
32 func (m *mcItem) Value() []byte { return m.value } | 31 func (m *mcItem) Value() []byte { return m.value } |
33 func (m *mcItem) Object() interface{} { return m.object } | 32 func (m *mcItem) Object() interface{} { return m.object } |
34 func (m *mcItem) Flags() uint32 { return m.flags } | 33 func (m *mcItem) Flags() uint32 { return m.flags } |
35 func (m *mcItem) Expiration() time.Duration { return m.expiration } | 34 func (m *mcItem) Expiration() time.Duration { return m.expiration } |
36 | 35 |
37 func (m *mcItem) SetKey(key string) gae.MCItem { | 36 func (m *mcItem) SetKey(key string) mc.Item { |
38 m.key = key | 37 m.key = key |
39 return m | 38 return m |
40 } | 39 } |
41 func (m *mcItem) SetValue(val []byte) gae.MCItem { | 40 func (m *mcItem) SetValue(val []byte) mc.Item { |
42 m.value = val | 41 m.value = val |
43 return m | 42 return m |
44 } | 43 } |
45 func (m *mcItem) SetObject(obj interface{}) gae.MCItem { | 44 func (m *mcItem) SetObject(obj interface{}) mc.Item { |
46 m.object = obj | 45 m.object = obj |
47 return m | 46 return m |
48 } | 47 } |
49 func (m *mcItem) SetFlags(flg uint32) gae.MCItem { | 48 func (m *mcItem) SetFlags(flg uint32) mc.Item { |
50 m.flags = flg | 49 m.flags = flg |
51 return m | 50 return m |
52 } | 51 } |
53 func (m *mcItem) SetExpiration(exp time.Duration) gae.MCItem { | 52 func (m *mcItem) SetExpiration(exp time.Duration) mc.Item { |
54 m.expiration = exp | 53 m.expiration = exp |
55 return m | 54 return m |
56 } | 55 } |
57 | 56 |
58 func (m *mcItem) duplicate() *mcItem { | 57 func (m *mcItem) duplicate() *mcItem { |
59 ret := mcItem{} | 58 ret := mcItem{} |
60 ret = *m | 59 ret = *m |
61 ret.value = make([]byte, len(m.value)) | 60 ret.value = make([]byte, len(m.value)) |
62 copy(ret.value, m.value) | 61 copy(ret.value, m.value) |
63 return &ret | 62 return &ret |
64 } | 63 } |
65 | 64 |
66 type memcacheData struct { | 65 type memcacheData struct { |
67 lock sync.Mutex | 66 lock sync.Mutex |
68 items map[string]*mcItem | 67 items map[string]*mcItem |
69 casID uint64 | 68 casID uint64 |
70 } | 69 } |
71 | 70 |
72 // memcacheImpl binds the current connection's memcache data to an | 71 // memcacheImpl binds the current connection's memcache data to an |
73 // implementation of {gae.Memcache, gae.Testable}. | 72 // implementation of {gae.Memcache, gae.Testable}. |
74 type memcacheImpl struct { | 73 type memcacheImpl struct { |
75 » gae.Memcache | 74 » mc.Interface |
76 | 75 |
77 data *memcacheData | 76 data *memcacheData |
78 ctx context.Context | 77 ctx context.Context |
79 } | 78 } |
80 | 79 |
81 var _ gae.Memcache = (*memcacheImpl)(nil) | 80 var _ mc.Interface = (*memcacheImpl)(nil) |
82 | 81 |
83 // useMC adds a gae.Memcache implementation to context, accessible | 82 // useMC adds a gae.Memcache implementation to context, accessible |
84 // by gae.GetMC(c) | 83 // by gae.GetMC(c) |
85 func useMC(c context.Context) context.Context { | 84 func useMC(c context.Context) context.Context { |
86 lck := sync.Mutex{} | 85 lck := sync.Mutex{} |
87 mcdMap := map[string]*memcacheData{} | 86 mcdMap := map[string]*memcacheData{} |
88 | 87 |
89 » return gae.SetMCFactory(c, func(ic context.Context) gae.Memcache { | 88 » return mc.SetFactory(c, func(ic context.Context) mc.Interface { |
90 lck.Lock() | 89 lck.Lock() |
91 defer lck.Unlock() | 90 defer lck.Unlock() |
92 | 91 |
93 ns := curGID(ic).namespace | 92 ns := curGID(ic).namespace |
94 mcd, ok := mcdMap[ns] | 93 mcd, ok := mcdMap[ns] |
95 if !ok { | 94 if !ok { |
96 mcd = &memcacheData{items: map[string]*mcItem{}} | 95 mcd = &memcacheData{items: map[string]*mcItem{}} |
97 mcdMap[ns] = mcd | 96 mcdMap[ns] = mcd |
98 } | 97 } |
99 | 98 |
100 return &memcacheImpl{ | 99 return &memcacheImpl{ |
101 » » » dummy.MC(), | 100 » » » dummy.Memcache(), |
102 mcd, | 101 mcd, |
103 ic, | 102 ic, |
104 } | 103 } |
105 }) | 104 }) |
106 } | 105 } |
107 | 106 |
108 func (m *memcacheImpl) mkItemLocked(i gae.MCItem) (ret *mcItem) { | 107 func (m *memcacheImpl) mkItemLocked(i mc.Item) (ret *mcItem) { |
109 m.data.casID++ | 108 m.data.casID++ |
110 | 109 |
111 var exp time.Duration | 110 var exp time.Duration |
112 if i.Expiration() != 0 { | 111 if i.Expiration() != 0 { |
113 exp = time.Duration(clock.Now(m.ctx).Add(i.Expiration()).UnixNan
o()) | 112 exp = time.Duration(clock.Now(m.ctx).Add(i.Expiration()).UnixNan
o()) |
114 } | 113 } |
115 newItem := mcItem{ | 114 newItem := mcItem{ |
116 key: i.Key(), | 115 key: i.Key(), |
117 flags: i.Flags(), | 116 flags: i.Flags(), |
118 expiration: exp, | 117 expiration: exp, |
119 value: i.Value(), | 118 value: i.Value(), |
120 CasID: m.data.casID, | 119 CasID: m.data.casID, |
121 } | 120 } |
122 return newItem.duplicate() | 121 return newItem.duplicate() |
123 } | 122 } |
124 | 123 |
125 func (m *memcacheImpl) NewItem(key string) gae.MCItem { | 124 func (m *memcacheImpl) NewItem(key string) mc.Item { |
126 return &mcItem{key: key} | 125 return &mcItem{key: key} |
127 } | 126 } |
128 | 127 |
129 // Add implements context.MCSingleReadWriter.Add. | 128 // Add implements context.MCSingleReadWriter.Add. |
130 func (m *memcacheImpl) Add(i gae.MCItem) error { | 129 func (m *memcacheImpl) Add(i mc.Item) error { |
131 m.data.lock.Lock() | 130 m.data.lock.Lock() |
132 defer m.data.lock.Unlock() | 131 defer m.data.lock.Unlock() |
133 | 132 |
134 if _, ok := m.retrieveLocked(i.Key()); !ok { | 133 if _, ok := m.retrieveLocked(i.Key()); !ok { |
135 m.data.items[i.Key()] = m.mkItemLocked(i) | 134 m.data.items[i.Key()] = m.mkItemLocked(i) |
136 return nil | 135 return nil |
137 } | 136 } |
138 » return gae.ErrMCNotStored | 137 » return mc.ErrNotStored |
139 } | 138 } |
140 | 139 |
141 // CompareAndSwap implements context.MCSingleReadWriter.CompareAndSwap. | 140 // CompareAndSwap implements context.MCSingleReadWriter.CompareAndSwap. |
142 func (m *memcacheImpl) CompareAndSwap(item gae.MCItem) error { | 141 func (m *memcacheImpl) CompareAndSwap(item mc.Item) error { |
143 m.data.lock.Lock() | 142 m.data.lock.Lock() |
144 defer m.data.lock.Unlock() | 143 defer m.data.lock.Unlock() |
145 | 144 |
146 if cur, ok := m.retrieveLocked(item.Key()); ok { | 145 if cur, ok := m.retrieveLocked(item.Key()); ok { |
147 casid := uint64(0) | 146 casid := uint64(0) |
148 if mi, ok := item.(*mcItem); ok && mi != nil { | 147 if mi, ok := item.(*mcItem); ok && mi != nil { |
149 casid = mi.CasID | 148 casid = mi.CasID |
150 } | 149 } |
151 | 150 |
152 if cur.CasID == casid { | 151 if cur.CasID == casid { |
153 m.data.items[item.Key()] = m.mkItemLocked(item) | 152 m.data.items[item.Key()] = m.mkItemLocked(item) |
154 } else { | 153 } else { |
155 » » » return gae.ErrMCCASConflict | 154 » » » return mc.ErrCASConflict |
156 } | 155 } |
157 } else { | 156 } else { |
158 » » return gae.ErrMCNotStored | 157 » » return mc.ErrNotStored |
159 } | 158 } |
160 return nil | 159 return nil |
161 } | 160 } |
162 | 161 |
163 // Set implements context.MCSingleReadWriter.Set. | 162 // Set implements context.MCSingleReadWriter.Set. |
164 func (m *memcacheImpl) Set(i gae.MCItem) error { | 163 func (m *memcacheImpl) Set(i mc.Item) error { |
165 m.data.lock.Lock() | 164 m.data.lock.Lock() |
166 defer m.data.lock.Unlock() | 165 defer m.data.lock.Unlock() |
167 m.data.items[i.Key()] = m.mkItemLocked(i) | 166 m.data.items[i.Key()] = m.mkItemLocked(i) |
168 return nil | 167 return nil |
169 } | 168 } |
170 | 169 |
171 // Get implements context.MCSingleReadWriter.Get. | 170 // Get implements context.MCSingleReadWriter.Get. |
172 func (m *memcacheImpl) Get(key string) (itm gae.MCItem, err error) { | 171 func (m *memcacheImpl) Get(key string) (itm mc.Item, err error) { |
173 m.data.lock.Lock() | 172 m.data.lock.Lock() |
174 defer m.data.lock.Unlock() | 173 defer m.data.lock.Unlock() |
175 if val, ok := m.retrieveLocked(key); ok { | 174 if val, ok := m.retrieveLocked(key); ok { |
176 itm = val.duplicate().SetExpiration(0) | 175 itm = val.duplicate().SetExpiration(0) |
177 } else { | 176 } else { |
178 » » err = gae.ErrMCCacheMiss | 177 » » err = mc.ErrCacheMiss |
179 } | 178 } |
180 return | 179 return |
181 } | 180 } |
182 | 181 |
183 // Delete implements context.MCSingleReadWriter.Delete. | 182 // Delete implements context.MCSingleReadWriter.Delete. |
184 func (m *memcacheImpl) Delete(key string) error { | 183 func (m *memcacheImpl) Delete(key string) error { |
185 m.data.lock.Lock() | 184 m.data.lock.Lock() |
186 defer m.data.lock.Unlock() | 185 defer m.data.lock.Unlock() |
187 | 186 |
188 if _, ok := m.retrieveLocked(key); ok { | 187 if _, ok := m.retrieveLocked(key); ok { |
189 delete(m.data.items, key) | 188 delete(m.data.items, key) |
190 return nil | 189 return nil |
191 } | 190 } |
192 » return gae.ErrMCCacheMiss | 191 » return mc.ErrCacheMiss |
193 } | 192 } |
194 | 193 |
195 func (m *memcacheImpl) retrieveLocked(key string) (*mcItem, bool) { | 194 func (m *memcacheImpl) retrieveLocked(key string) (*mcItem, bool) { |
196 ret, ok := m.data.items[key] | 195 ret, ok := m.data.items[key] |
197 if ok && ret.Expiration() != 0 && ret.Expiration() < time.Duration(clock
.Now(m.ctx).UnixNano()) { | 196 if ok && ret.Expiration() != 0 && ret.Expiration() < time.Duration(clock
.Now(m.ctx).UnixNano()) { |
198 ret = nil | 197 ret = nil |
199 ok = false | 198 ok = false |
200 delete(m.data.items, key) | 199 delete(m.data.items, key) |
201 } | 200 } |
202 return ret, ok | 201 return ret, ok |
203 } | 202 } |
OLD | NEW |