OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package memory | |
6 | |
7 import ( | |
8 "infra/gae/libs/wrapper" | |
9 "infra/gae/libs/wrapper/gae/commonErrors" | |
10 "infra/gae/libs/wrapper/unsafe" | |
11 "infra/libs/clock" | |
12 "sync" | |
13 "time" | |
14 | |
15 "golang.org/x/net/context" | |
16 | |
17 "appengine/memcache" | |
18 ) | |
19 | |
20 type memcacheData struct { | |
21 wrapper.BrokenFeatures | |
22 | |
23 lock sync.Mutex | |
24 items map[string]*unsafe.Item | |
25 casID uint64 | |
26 } | |
27 | |
28 // memcacheImpl binds the current connection's memcache data to an | |
29 // implementation of {wrapper.Memcache, wrapper.Testable}. | |
30 type memcacheImpl struct { | |
31 wrapper.Memcache | |
32 | |
33 data *memcacheData | |
34 ctx context.Context | |
35 } | |
36 | |
37 var ( | |
38 _ = wrapper.Memcache((*memcacheImpl)(nil)) | |
39 _ = wrapper.Testable((*memcacheImpl)(nil)) | |
40 ) | |
41 | |
42 // useMC adds a wrapper.Memcache implementation to context, accessible | |
43 // by wrapper.GetMC(c) | |
44 func useMC(c context.Context) context.Context { | |
45 lck := sync.Mutex{} | |
46 mcdMap := map[string]*memcacheData{} | |
47 | |
48 return wrapper.SetMCFactory(c, func(ic context.Context) wrapper.Memcache
{ | |
49 lck.Lock() | |
50 defer lck.Unlock() | |
51 | |
52 ns := curGID(ic).namespace | |
53 mcd, ok := mcdMap[ns] | |
54 if !ok { | |
55 mcd = &memcacheData{ | |
56 BrokenFeatures: wrapper.BrokenFeatures{ | |
57 DefaultError: commonErrors.ErrServerErro
rMC}, | |
58 items: map[string]*unsafe.Item{}} | |
59 mcdMap[ns] = mcd | |
60 } | |
61 | |
62 return &memcacheImpl{ | |
63 wrapper.DummyMC(), | |
64 mcd, | |
65 ic, | |
66 } | |
67 }) | |
68 } | |
69 | |
70 func (m *memcacheImpl) mkItemLocked(i *memcache.Item) *unsafe.Item { | |
71 m.data.casID++ | |
72 var exp time.Duration | |
73 if i.Expiration != 0 { | |
74 exp = time.Duration(clock.Now(m.ctx).Add(i.Expiration).UnixNano(
)) | |
75 } | |
76 newItem := unsafe.Item{ | |
77 Key: i.Key, | |
78 Value: make([]byte, len(i.Value)), | |
79 Flags: i.Flags, | |
80 Expiration: exp, | |
81 CasID: m.data.casID, | |
82 } | |
83 copy(newItem.Value, i.Value) | |
84 return &newItem | |
85 } | |
86 | |
87 func copyBack(i *unsafe.Item) *memcache.Item { | |
88 ret := &memcache.Item{ | |
89 Key: i.Key, | |
90 Value: make([]byte, len(i.Value)), | |
91 Flags: i.Flags, | |
92 } | |
93 copy(ret.Value, i.Value) | |
94 unsafe.MCSetCasID(ret, i.CasID) | |
95 | |
96 return ret | |
97 } | |
98 | |
99 func (m *memcacheImpl) retrieve(key string) (*unsafe.Item, bool) { | |
100 ret, ok := m.data.items[key] | |
101 if ok && ret.Expiration != 0 && ret.Expiration < time.Duration(clock.Now
(m.ctx).UnixNano()) { | |
102 ret = nil | |
103 ok = false | |
104 delete(m.data.items, key) | |
105 } | |
106 return ret, ok | |
107 } | |
108 | |
109 func (m *memcacheImpl) BreakFeatures(err error, features ...string) { | |
110 m.data.BreakFeatures(err, features...) | |
111 } | |
112 | |
113 func (m *memcacheImpl) UnbreakFeatures(features ...string) { | |
114 m.data.UnbreakFeatures(features...) | |
115 } | |
116 | |
117 // Add implements context.MCSingleReadWriter.Add. | |
118 func (m *memcacheImpl) Add(i *memcache.Item) error { | |
119 if err := m.data.IsBroken(); err != nil { | |
120 return err | |
121 } | |
122 | |
123 m.data.lock.Lock() | |
124 defer m.data.lock.Unlock() | |
125 | |
126 if _, ok := m.retrieve(i.Key); !ok { | |
127 m.data.items[i.Key] = m.mkItemLocked(i) | |
128 return nil | |
129 } | |
130 return memcache.ErrNotStored | |
131 } | |
132 | |
133 // CompareAndSwap implements context.MCSingleReadWriter.CompareAndSwap. | |
134 func (m *memcacheImpl) CompareAndSwap(item *memcache.Item) error { | |
135 if err := m.data.IsBroken(); err != nil { | |
136 return err | |
137 } | |
138 | |
139 m.data.lock.Lock() | |
140 defer m.data.lock.Unlock() | |
141 | |
142 if cur, ok := m.retrieve(item.Key); ok { | |
143 if cur.CasID == unsafe.MCGetCasID(item) { | |
144 m.data.items[item.Key] = m.mkItemLocked(item) | |
145 } else { | |
146 return memcache.ErrCASConflict | |
147 } | |
148 } else { | |
149 return memcache.ErrNotStored | |
150 } | |
151 return nil | |
152 } | |
153 | |
154 // Set implements context.MCSingleReadWriter.Set. | |
155 func (m *memcacheImpl) Set(i *memcache.Item) error { | |
156 if err := m.data.IsBroken(); err != nil { | |
157 return err | |
158 } | |
159 | |
160 m.data.lock.Lock() | |
161 defer m.data.lock.Unlock() | |
162 | |
163 m.data.items[i.Key] = m.mkItemLocked(i) | |
164 return nil | |
165 } | |
166 | |
167 // Get implements context.MCSingleReadWriter.Get. | |
168 func (m *memcacheImpl) Get(key string) (*memcache.Item, error) { | |
169 if err := m.data.IsBroken(); err != nil { | |
170 return nil, err | |
171 } | |
172 | |
173 m.data.lock.Lock() | |
174 defer m.data.lock.Unlock() | |
175 | |
176 if val, ok := m.retrieve(key); ok { | |
177 return copyBack(val), nil | |
178 } | |
179 return nil, memcache.ErrCacheMiss | |
180 } | |
181 | |
182 // Delete implements context.MCSingleReadWriter.Delete. | |
183 func (m *memcacheImpl) Delete(key string) error { | |
184 if err := m.data.IsBroken(); err != nil { | |
185 return err | |
186 } | |
187 | |
188 m.data.lock.Lock() | |
189 defer m.data.lock.Unlock() | |
190 | |
191 if _, ok := m.retrieve(key); ok { | |
192 delete(m.data.items, key) | |
193 return nil | |
194 } | |
195 return memcache.ErrCacheMiss | |
196 } | |
OLD | NEW |