Chromium Code Reviews| 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 |