Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: go/src/infra/gae/libs/wrapper/memory/datastore_data.go

Issue 1152383003: Simple memory testing for gae/wrapper (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@better_context_lite
Patch Set: final fixes? Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "errors"
9 "infra/gae/libs/wrapper"
10 goon_internal "infra/gae/libs/wrapper/memory/internal/goon"
11 "math/rand"
12 "sync"
13 "sync/atomic"
14
15 "github.com/mjibson/goon"
16
17 "appengine/datastore"
18 pb "appengine_internal/datastore"
19 )
20
21 ////////////////////////////////// knrKeeper ///////////////////////////////////
22
23 type knrKeeper struct {
24 knrLock sync.Mutex
25 knrFunc goon.KindNameResolver
26 }
27
28 func (k *knrKeeper) KindNameResolver() goon.KindNameResolver {
29 k.knrLock.Lock()
30 defer k.knrLock.Unlock()
31 ret := k.knrFunc
M-A Ruel 2015/05/29 02:01:22 if k.knrFunc == nil { k.knrFunc = goond.DefaultK
iannucci 2015/05/29 02:42:27 done
32 if ret == nil {
33 ret = goon.DefaultKindName
34 k.knrFunc = ret
35 }
36 return ret
37 }
38
39 func (k *knrKeeper) SetKindNameResolver(knr goon.KindNameResolver) {
M-A Ruel 2015/05/29 02:01:23 What's the use case?
iannucci 2015/05/29 02:42:28 It's the function that can take an object-to-be-se
40 k.knrLock.Lock()
41 defer k.knrLock.Unlock()
42 if knr == nil {
43 knr = goon.DefaultKindName
44 }
45 k.knrFunc = knr
46 }
47
48 //////////////////////////////// dataStoreData /////////////////////////////////
49
50 type dataStoreData struct {
51 wrapper.BrokenFeatures
52 knrKeeper
53
54 rwlock sync.RWMutex
55 // See README.md for store schema.
56 store *memStore
57 snap *memStore
58 }
59
60 ////////////////////////////// New(dataStoreData) //////////////////////////////
61
62 func newDataStoreData() *dataStoreData {
63 store := newMemStore()
64 return &dataStoreData{
65 BrokenFeatures: wrapper.BrokenFeatures{DefaultError: newDSError( pb.Error_INTERNAL_ERROR)},
66 store: store,
67 snap: store.Snapshot(), // empty but better than a nil pointer.
68 }
69 }
70
71 ////////////////////////////// Locker(Datastore) ///////////////////////////////
72
73 func (d *dataStoreData) Lock() {
74 d.rwlock.Lock()
75 }
76
77 func (d *dataStoreData) Unlock() {
78 d.rwlock.Unlock()
79 }
80
81 func groupMetaKey(key *datastore.Key) []byte {
82 return keyBytes(noNS, newKey("", "__entity_group__", "", 1, rootKey(key) ))
83 }
84
85 func groupIDsKey(key *datastore.Key) []byte {
86 return keyBytes(noNS, newKey("", "__entity_group_ids__", "", 1, rootKey( key)))
87 }
88
89 func rootIDsKey(kind string) []byte {
90 return keyBytes(noNS, newKey("", "__entity_root_ids__", kind, 0, nil))
91 }
92
93 func curVersion(ents *memCollection, key []byte) (int64, error) {
94 var err error
95 v := ents.Get(key)
96 num := int64(0)
97 numData := &propertyList{}
98 if v != nil {
99 err = numData.UnmarshalBinary(v)
M-A Ruel 2015/05/29 02:01:23 if v := ents.Get(key); v != nil { numData := &pr
iannucci 2015/05/29 02:42:27 done
100 num = (*numData)[0].Value.(int64)
101 }
102 return num, err
103 }
104
105 func incrementLocked(ents *memCollection, key []byte) (int64, error) {
106 v := ents.Get(key)
107
108 num := int64(0)
109 numData := &propertyList{}
110 if v == nil {
M-A Ruel 2015/05/29 02:01:22 if v := ents.Get(key); v != nil { then switch gro
iannucci 2015/05/29 02:42:28 done
111 *numData = append(*numData, datastore.Property{Name: "__version_ _"})
112 } else {
113 if err := numData.UnmarshalBinary(v); err != nil {
114 return 0, err
115 }
116 num = (*numData)[0].Value.(int64)
117 }
118 num++
119 (*numData)[0].Value = num
120 incData, err := numData.MarshalBinary()
121 if err != nil {
122 return 0, err
123 }
124 ents.Set(key, incData)
125
126 return num, nil
127 }
128
129 func (d *dataStoreData) entsKeyLocked(key *datastore.Key) (*memCollection, *data store.Key, error) {
130 coll := "ents:" + key.Namespace()
131 ents := d.store.GetCollection(coll)
132 if ents == nil {
133 ents = d.store.SetCollection(coll, nil)
134 }
135
136 if key.Incomplete() {
137 var idKey []byte
138 if key.Parent() == nil {
139 idKey = rootIDsKey(key.Kind())
140 } else {
141 idKey = groupIDsKey(key)
142 }
143
144 id, err := incrementLocked(ents, idKey)
145 if err != nil {
146 return nil, nil, err
147 }
148 key = newKey(key.Namespace(), key.Kind(), "", id, key.Parent())
149 }
150
151 return ents, key, nil
152 }
153
154 func putPrelim(ns string, knr goon.KindNameResolver, src interface{}) (*datastor e.Key, *propertyList, error) {
155 key := newKeyObj(ns, knr, src)
156 if !KeyCouldBeValid(ns, key, UserKeyOnly) {
157 // TODO(riannucci): different error for Put-ing to reserved Keys ?
158 return nil, nil, datastore.ErrInvalidKey
159 }
160
161 data, err := toPL(src)
162 return key, data, err
163 }
164
165 func (d *dataStoreData) put(ns string, src interface{}) (*datastore.Key, error) {
166 key, plData, err := putPrelim(ns, d.KindNameResolver(), src)
167 if err != nil {
168 return nil, err
169 }
170 key, err = d.putInner(key, plData)
171 if err != nil {
M-A Ruel 2015/05/29 02:01:23 if key, err = d.putInner(key, plData); err != nil
iannucci 2015/05/29 02:42:27 done
172 return nil, err
173 }
174 return key, goon_internal.SetStructKey(src, key, d.KindNameResolver())
175 }
176
177 func (d *dataStoreData) putInner(key *datastore.Key, data *propertyList) (*datas tore.Key, error) {
178 dataBytes, err := data.MarshalBinary()
179 if err != nil {
180 return nil, err
181 }
182
183 d.Lock()
184 defer d.Unlock()
185
186 ents, key, err := d.entsKeyLocked(key)
187 if err != nil {
188 return nil, err
189 }
190
M-A Ruel 2015/05/29 02:01:23 I don't think this much spacing is needed here and
iannucci 2015/05/29 02:42:28 done
191 if _, err = incrementLocked(ents, groupMetaKey(key)); err != nil {
192 return nil, err
193 }
194
195 ents.Set(keyBytes(noNS, key), dataBytes)
196
197 return key, nil
198 }
199
200 func getInner(ns string, knr goon.KindNameResolver, dst interface{}, getColl fun c(*datastore.Key) (*memCollection, error)) error {
201 key := newKeyObj(ns, knr, dst)
202 if !KeyValid(ns, key, AllowSpecialKeys) {
203 return datastore.ErrInvalidKey
204 }
205
206 ents, err := getColl(key)
207 if err != nil {
208 return err
209 }
210 if ents == nil {
211 return datastore.ErrNoSuchEntity
212 }
213 pdata := ents.Get(keyBytes(noNS, key))
214 if pdata == nil {
215 return datastore.ErrNoSuchEntity
216 }
217 pl := &propertyList{}
218 if err = pl.UnmarshalBinary(pdata); err != nil {
219 return err
220 }
221 return fromPL(pl, dst)
222 }
223
224 func (d *dataStoreData) get(ns string, dst interface{}) error {
225 return getInner(ns, d.KindNameResolver(), dst, func(*datastore.Key) (*me mCollection, error) {
226 d.rwlock.RLock()
227 s := d.store.Snapshot()
228 d.rwlock.RUnlock()
229
230 return s.GetCollection("ents:" + ns), nil
231 })
232 }
233
234 func (d *dataStoreData) del(ns string, key *datastore.Key) error {
235 if !KeyValid(ns, key, UserKeyOnly) {
236 return datastore.ErrInvalidKey
237 }
238
239 keyBuf := keyBytes(noNS, key)
240
241 d.Lock()
242 defer d.Unlock()
243
244 ents := d.store.GetCollection("ents:" + ns)
245 if ents == nil {
246 return nil
247 }
248
249 if _, err := incrementLocked(ents, groupMetaKey(key)); err != nil {
250 return err
251 }
252
253 ents.Delete(keyBuf)
254 return nil
255 }
256
257 ///////////////////////// memContextObj(dataStoreData) /////////////////////////
258
259 func (d *dataStoreData) canApplyTxn(obj memContextObj) bool {
260 // TODO(riannucci): implement with Flush/FlushRevert for persistance.
261
262 txn := obj.(*txnDataStoreData)
263 for rk, muts := range txn.muts {
264 if len(muts) == 0 { // read-only
265 continue
266 }
267 k, err := keyFromByteString(withNS, rk)
268 if err != nil {
269 panic(err)
270 }
271 entKey := "ents:" + k.Namespace()
272 mkey := groupMetaKey(k)
273 entsHead := d.store.GetCollection(entKey)
274 entsSnap := txn.snap.GetCollection(entKey)
275 vHead, err := curVersion(entsHead, mkey)
276 if err != nil {
277 panic(err)
278 }
279 vSnap, err := curVersion(entsSnap, mkey)
280 if err != nil {
281 panic(err)
282 }
283 if vHead != vSnap {
284 return false
285 }
286 }
287 return true
288 }
289
290 func (d *dataStoreData) applyTxn(r *rand.Rand, obj memContextObj) {
291 txn := obj.(*txnDataStoreData)
292 for _, muts := range txn.muts {
293 if len(muts) == 0 { // read-only
294 continue
295 }
296 for _, m := range muts {
297 var err error
298 if m.data == nil {
299 err = d.del(m.key.Namespace(), m.key)
300 } else {
301 _, err = d.putInner(m.key, m.data)
302 }
303 if err != nil {
304 panic(err)
305 }
306 }
307 }
308 }
309
310 func (d *dataStoreData) mkTxn(o *datastore.TransactionOptions) (memContextObj, e rror) {
311 return &txnDataStoreData{
312 // alias to the main datastore's so that testing code can have p rimitive
313 // access to break features inside of transactions.
314 BrokenFeatures: &d.BrokenFeatures,
315 parent: d,
316 knrKeeper: knrKeeper{knrFunc: d.knrFunc},
317 isXG: o != nil && o.XG,
318 snap: d.store.Snapshot(),
319 muts: map[string][]txnMutation{},
320 }, nil
321 }
322
323 func (d *dataStoreData) endTxn() {}
324
325 /////////////////////////////// txnDataStoreData ///////////////////////////////
326 type txnMutation struct {
327 key *datastore.Key
328 data *propertyList
329 }
330
331 type txnDataStoreData struct {
332 *wrapper.BrokenFeatures
M-A Ruel 2015/05/29 02:01:23 Does it need to be a pointer?
iannucci 2015/05/29 02:42:27 yes
333 knrKeeper
334 sync.Mutex
335
336 parent *dataStoreData
337
338 // boolean 0 or 1, use atomic.*Int32 to access.
339 closed int32
340 isXG bool
341
342 snap *memStore
343
344 // string is the raw-bytes encoding of the entity root incl. namespace
345 muts map[string][]txnMutation
346 // TODO(riannucci): account for 'transaction size' limit of 10MB by summ ing
347 // length of encoded keys + values.
348 }
349
350 const xgEGLimit = 25
351
352 /////////////////////// memContextObj(txnDataStoreData) ////////////////////////
353
354 func (*txnDataStoreData) canApplyTxn(memContextObj) bool { return false }
355 func (td *txnDataStoreData) endTxn() {
356 if atomic.LoadInt32(&td.closed) == 1 {
357 panic("cannot end transaction twice")
358 }
359 atomic.StoreInt32(&td.closed, 1)
360 }
361 func (*txnDataStoreData) applyTxn(*rand.Rand, memContextObj) {
362 panic("txnDataStoreData cannot apply transactions")
363 }
364 func (*txnDataStoreData) mkTxn(*datastore.TransactionOptions) (memContextObj, er ror) {
365 return nil, errors.New("datastore: nested transactions are not supported ")
366 }
367
368 /////////////////// wrapper.BrokenFeatures(txnDataStoreData) ///////////////////
369
370 func (td *txnDataStoreData) IsBroken() error {
371 // Slightly different from the SDK... datastore and taskqueue each imple ment
372 // this here, where in the SDK only datastore.transaction.Call does.
373 if atomic.LoadInt32(&td.closed) == 1 {
374 return errors.New("datastore: transaction context has expired")
375 }
376 return td.BrokenFeatures.IsBroken()
377 }
378
379 // writeMutation ensures that this transaction can support the given key/value
380 // mutation.
381 //
382 // if getOnly is true, don't record the actual mutation data, just ensure that
383 // the key is in an included entity group (or add an empty entry for tha t
384 // group).
385 //
386 // if !getOnly && data == nil, this counts as a deletion instead of a Put.
387 //
388 // Returns an error if this key causes the transaction to cross too many entity
389 // groups.
390 func (td *txnDataStoreData) writeMutation(getOnly bool, key *datastore.Key, data *propertyList) error {
391 rk := string(keyBytes(withNS, rootKey(key)))
392
393 td.Lock()
394 defer td.Unlock()
395
396 if _, ok := td.muts[rk]; !ok {
397 limit := 1
398 if td.isXG {
399 limit = xgEGLimit
400 }
401 if len(td.muts)+1 > limit {
402 msg := "cross-group transaction need to be explicitly sp ecified (xg=True)"
403 if td.isXG {
404 msg = "operating on too many entity groups in a single transaction"
405 }
406 return newDSError(pb.Error_BAD_REQUEST, msg)
407 }
408 td.muts[rk] = []txnMutation{}
409 }
410 if !getOnly {
411 td.muts[rk] = append(td.muts[rk], txnMutation{key, data})
412 }
413
414 return nil
415 }
416
417 func (td *txnDataStoreData) put(ns string, src interface{}) (*datastore.Key, err or) {
418 key, plData, err := putPrelim(ns, td.KindNameResolver(), src)
419 if err != nil {
420 return nil, err
421 }
422
423 func() {
424 td.parent.Lock()
425 defer td.parent.Unlock()
426 _, key, err = td.parent.entsKeyLocked(key)
427 }()
428 if err != nil {
429 return nil, err
430 }
431
432 if err = td.writeMutation(false, key, plData); err != nil {
433 return nil, err
434 }
435
436 return key, goon_internal.SetStructKey(src, key, td.KindNameResolver())
437 }
438
439 func (td *txnDataStoreData) get(ns string, dst interface{}) error {
440 return getInner(ns, td.KindNameResolver(), dst, func(key *datastore.Key) (*memCollection, error) {
441 if err := td.writeMutation(true, key, nil); err != nil {
442 return nil, err
443 }
444 return td.snap.GetCollection("ents:" + ns), nil
445 })
446 }
447
448 func (td *txnDataStoreData) del(ns string, key *datastore.Key) error {
449 if !KeyValid(ns, key, UserKeyOnly) {
450 return datastore.ErrInvalidKey
451 }
452 return td.writeMutation(false, key, nil)
453 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698