OLD | NEW |
---|---|
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "encoding/binary" | 8 "encoding/binary" |
9 "sync" | 9 "sync" |
10 "time" | 10 "time" |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
71 func (m *mcDataItem) toUserItem(key string) *mcItem { | 71 func (m *mcDataItem) toUserItem(key string) *mcItem { |
72 value := make([]byte, len(m.value)) | 72 value := make([]byte, len(m.value)) |
73 copy(value, m.value) | 73 copy(value, m.value) |
74 // Expiration is defined to be 0 when retrieving items from memcache. | 74 // Expiration is defined to be 0 when retrieving items from memcache. |
75 // https://cloud.google.com/appengine/docs/go/memcache/reference#Item | 75 // https://cloud.google.com/appengine/docs/go/memcache/reference#Item |
76 // ¯\_(ツ)_/¯ | 76 // ¯\_(ツ)_/¯ |
77 return &mcItem{key, value, m.flags, 0, m.casID} | 77 return &mcItem{key, value, m.flags, 0, m.casID} |
78 } | 78 } |
79 | 79 |
80 type memcacheData struct { | 80 type memcacheData struct { |
81 » lock sync.RWMutex | 81 » lock sync.Mutex |
82 items map[string]*mcDataItem | 82 items map[string]*mcDataItem |
83 casID uint64 | 83 casID uint64 |
84 | 84 |
85 stats mc.Statistics | 85 stats mc.Statistics |
86 } | 86 } |
87 | 87 |
88 func (m *memcacheData) mkDataItemLocked(now time.Time, i mc.Item) (ret *mcDataIt em) { | 88 func (m *memcacheData) mkDataItemLocked(now time.Time, i mc.Item) (ret *mcDataIt em) { |
89 m.casID++ | 89 m.casID++ |
90 | 90 |
91 exp := time.Time{} | 91 exp := time.Time{} |
92 if i.Expiration() != 0 { | 92 if i.Expiration() != 0 { |
93 exp = now.Add(i.Expiration()).Truncate(time.Second) | 93 exp = now.Add(i.Expiration()).Truncate(time.Second) |
94 } | 94 } |
95 value := make([]byte, len(i.Value())) | 95 value := make([]byte, len(i.Value())) |
96 copy(value, i.Value()) | 96 copy(value, i.Value()) |
97 return &mcDataItem{ | 97 return &mcDataItem{ |
98 flags: i.Flags(), | 98 flags: i.Flags(), |
99 expiration: exp, | 99 expiration: exp, |
100 value: value, | 100 value: value, |
101 casID: m.casID, | 101 casID: m.casID, |
102 } | 102 } |
103 } | 103 } |
104 | 104 |
105 func (m *memcacheData) setItemLocked(now time.Time, i mc.Item) { | 105 func (m *memcacheData) setItemLocked(now time.Time, i mc.Item) { |
106 if cur, ok := m.items[i.Key()]; ok { | 106 if cur, ok := m.items[i.Key()]; ok { |
107 m.stats.Items-- | 107 m.stats.Items-- |
108 m.stats.Bytes -= uint64(len(cur.value)) | 108 m.stats.Bytes -= uint64(len(cur.value)) |
109 } | 109 } |
110 m.stats.Items++ | 110 m.stats.Items++ |
dnj
2016/12/06 17:24:06
All mutations to "m.stats" while holding RLock are
| |
111 m.stats.Bytes += uint64(len(i.Value())) | 111 m.stats.Bytes += uint64(len(i.Value())) |
112 m.items[i.Key()] = m.mkDataItemLocked(now, i) | 112 m.items[i.Key()] = m.mkDataItemLocked(now, i) |
113 } | 113 } |
114 | 114 |
115 func (m *memcacheData) delItemLocked(k string) { | 115 func (m *memcacheData) delItemLocked(k string) { |
116 if itm, ok := m.items[k]; ok { | 116 if itm, ok := m.items[k]; ok { |
117 m.stats.Items-- | 117 m.stats.Items-- |
118 m.stats.Bytes -= uint64(len(itm.value)) | 118 m.stats.Bytes -= uint64(len(itm.value)) |
119 delete(m.items, k) | 119 delete(m.items, k) |
120 } | 120 } |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
251 } | 251 } |
252 | 252 |
253 func (m *memcacheImpl) GetMulti(keys []string, cb mc.RawItemCB) error { | 253 func (m *memcacheImpl) GetMulti(keys []string, cb mc.RawItemCB) error { |
254 now := clock.Now(m.ctx) | 254 now := clock.Now(m.ctx) |
255 | 255 |
256 itms := make([]mc.Item, len(keys)) | 256 itms := make([]mc.Item, len(keys)) |
257 errs := make([]error, len(keys)) | 257 errs := make([]error, len(keys)) |
258 | 258 |
259 for i, k := range keys { | 259 for i, k := range keys { |
260 itms[i], errs[i] = func() (mc.Item, error) { | 260 itms[i], errs[i] = func() (mc.Item, error) { |
261 » » » m.data.lock.RLock() | 261 » » » m.data.lock.Lock() |
262 » » » defer m.data.lock.RUnlock() | 262 » » » defer m.data.lock.Unlock() |
263 val, err := m.data.retrieveLocked(now, k) | 263 val, err := m.data.retrieveLocked(now, k) |
264 if err != nil { | 264 if err != nil { |
265 return nil, err | 265 return nil, err |
266 } | 266 } |
267 return val.toUserItem(k), nil | 267 return val.toUserItem(k), nil |
268 }() | 268 }() |
269 } | 269 } |
270 | 270 |
271 for i, itm := range itms { | 271 for i, itm := range itms { |
272 cb(itm, errs[i]) | 272 cb(itm, errs[i]) |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 } | 338 } |
339 | 339 |
340 newval := make([]byte, 8) | 340 newval := make([]byte, 8) |
341 binary.LittleEndian.PutUint64(newval, cur) | 341 binary.LittleEndian.PutUint64(newval, cur) |
342 m.data.setItemLocked(now, m.NewItem(key).SetValue(newval)) | 342 m.data.setItemLocked(now, m.NewItem(key).SetValue(newval)) |
343 | 343 |
344 return cur, nil | 344 return cur, nil |
345 } | 345 } |
346 | 346 |
347 func (m *memcacheImpl) Stats() (*mc.Statistics, error) { | 347 func (m *memcacheImpl) Stats() (*mc.Statistics, error) { |
348 » m.data.lock.RLock() | 348 » m.data.lock.Lock() |
349 » defer m.data.lock.RUnlock() | 349 » defer m.data.lock.Unlock() |
350 | 350 |
351 ret := m.data.stats | 351 ret := m.data.stats |
352 return &ret, nil | 352 return &ret, nil |
353 } | 353 } |
OLD | NEW |