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

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

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

Powered by Google App Engine
This is Rietveld 408576698