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

Side by Side Diff: filter/txnBuf/txnbuf_test.go

Issue 1309803004: Add transaction buffer filter. (Closed) Base URL: https://github.com/luci/gae.git@add_query_support
Patch Set: change pcg timeout and codereview server to crcr.as.com Created 5 years, 2 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
« no previous file with comments | « filter/txnBuf/state.go ('k') | impl/memory/datastore_data.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 txnBuf
6
7 import (
8 "bytes"
9 "fmt"
10 "math/rand"
11 "testing"
12
13 "github.com/luci/gae/filter/count"
14 "github.com/luci/gae/impl/memory"
15 "github.com/luci/gae/service/datastore"
16 "github.com/luci/luci-go/common/cmpbin"
17 "github.com/luci/luci-go/common/errors"
18 . "github.com/luci/luci-go/common/testing/assertions"
19 . "github.com/smartystreets/goconvey/convey"
20 "golang.org/x/net/context"
21 )
22
23 type Foo struct {
24 ID int64 `gae:"$id"`
25 Parent *datastore.Key `gae:"$parent"`
26
27 Value []int64
28 ValueNI []byte `gae:",noindex"`
29 Sort []string
30 }
31
32 func toIntSlice(stuff []interface{}) []int64 {
33 vals, ok := stuff[0].([]int64)
34 if !ok {
35 vals = make([]int64, len(stuff))
36 for i := range vals {
37 vals[i] = int64(stuff[i].(int))
38 }
39 }
40 return vals
41 }
42
43 func toInt64(thing interface{}) int64 {
44 switch x := thing.(type) {
45 case int:
46 return int64(x)
47 case int64:
48 return x
49 default:
50 panic(fmt.Errorf("wat r it? %v", x))
51 }
52 }
53
54 func fooShouldHave(ds datastore.Interface) func(interface{}, ...interface{}) str ing {
55 return func(id interface{}, values ...interface{}) string {
56 f := &Foo{ID: toInt64(id)}
57 err := ds.Get(f)
58 if len(values) == 0 {
59 return ShouldEqual(err, datastore.ErrNoSuchEntity)
60 }
61
62 ret := ShouldBeNil(err)
63 if ret == "" {
64 if data, ok := values[0].([]byte); ok {
65 ret = ShouldResemble(f.ValueNI, data)
66 } else {
67 ret = ShouldResemble(f.Value, toIntSlice(values) )
68 }
69 }
70 return ret
71 }
72 }
73
74 func fooSetTo(ds datastore.Interface) func(interface{}, ...interface{}) string {
75 return func(id interface{}, values ...interface{}) string {
76 f := &Foo{ID: toInt64(id)}
77 if len(values) == 0 {
78 return ShouldBeNil(ds.Delete(ds.KeyForObj(f)))
79 }
80 if data, ok := values[0].([]byte); ok {
81 f.ValueNI = data
82 } else {
83 f.Value = toIntSlice(values)
84 }
85 return ShouldBeNil(ds.Put(f))
86 }
87 }
88
89 var (
90 dataMultiRoot = make([]*Foo, 20)
91 dataSingleRoot = make([]*Foo, 20)
92 hugeField = make([]byte, DefaultSizeBudget/8)
93 hugeData = make([]*Foo, 11)
94 root = datastore.MakeKey("something~else", "", "Parent", 1)
95 )
96
97 func init() {
98 cb := func(i int64) string {
99 buf := &bytes.Buffer{}
100 cmpbin.WriteInt(buf, i)
101 return buf.String()
102 }
103
104 rs := rand.NewSource(0)
105 root := datastore.MakeKey("something~else", "", "Parent", 1)
106 nums := make([]string, 20)
107 for i := range dataMultiRoot {
108 id := int64(i + 1)
109 nums[i] = cb(id)
110
111 val := make([]int64, rs.Int63()%20)
112 for j := range val {
113 r := rs.Int63()
114 val[j] = r
115 }
116
117 dataMultiRoot[i] = &Foo{ID: id, Value: val}
118 dataSingleRoot[i] = &Foo{ID: id, Parent: root, Value: val}
119 }
120
121 for i := range hugeField {
122 hugeField[i] = byte(i)
123 }
124
125 for i := range hugeData {
126 hugeData[i] = &Foo{ID: int64(i + 1), ValueNI: hugeField}
127 }
128 }
129
130 func mkds(data []*Foo) (under, over *count.DSCounter, ds datastore.Interface) {
131 c := memory.UseWithAppID(context.Background(), "something~else")
132 ds = datastore.Get(c)
133 _, err := ds.AllocateIDs(ds.KeyForObj(data[0]), 100)
134 if err != nil {
135 panic(err)
136 }
137 if err := ds.PutMulti(data); err != nil {
138 panic(err)
139 }
140
141 c, under = count.FilterRDS(c)
142 c = FilterRDS(c)
143 c, over = count.FilterRDS(c)
144 ds = datastore.Get(c)
145 return
146 }
147
148 func TestTransactionBuffers(t *testing.T) {
149 t.Parallel()
150
151 Convey("Get/Put/Delete", t, func() {
152 under, over, ds := mkds(dataMultiRoot)
153 ds.Testable().SetTransactionRetryCount(1)
154
155 So(under.PutMulti.Total(), ShouldEqual, 0)
156 So(over.PutMulti.Total(), ShouldEqual, 0)
157
158 Convey("Good", func() {
159 Convey("read-only", func() {
160 So(ds.RunInTransaction(func(c context.Context) e rror {
161 ds := datastore.Get(c)
162
163 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
164 return nil
165 }, nil), ShouldBeNil)
166 })
167
168 Convey("single-level read/write", func() {
169 So(ds.RunInTransaction(func(c context.Context) e rror {
170 ds := datastore.Get(c)
171
172 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
173
174 So(4, fooSetTo(ds), 1, 2, 3, 4)
175
176 So(3, fooSetTo(ds), 1, 2, 3, 4)
177
178 // look! it remembers :)
179 So(4, fooShouldHave(ds), 1, 2, 3, 4)
180 return nil
181 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
182
183 // 2 because we are simulating a transaction fai lure
184 So(under.PutMulti.Total(), ShouldEqual, 2)
185
186 So(3, fooShouldHave(ds), 1, 2, 3, 4)
187 So(4, fooShouldHave(ds), 1, 2, 3, 4)
188 })
189
190 Convey("multi-level read/write", func() {
191 So(ds.RunInTransaction(func(c context.Context) e rror {
192 ds := datastore.Get(c)
193
194 So(3, fooShouldHave(ds), dataMultiRoot[2 ].Value)
195
196 So(3, fooSetTo(ds), 1, 2, 3, 4)
197 So(7, fooSetTo(ds))
198
199 vals := []*Foo{
200 {ID: 793},
201 {ID: 7},
202 {ID: 3},
203 {ID: 4},
204 }
205 So(ds.GetMulti(vals), ShouldResemble, er rors.NewMultiError(
206 datastore.ErrNoSuchEntity,
207 datastore.ErrNoSuchEntity,
208 nil,
209 nil,
210 ))
211
212 So(vals[0].Value, ShouldBeNil)
213 So(vals[1].Value, ShouldBeNil)
214 So(vals[2].Value, ShouldResemble, []int6 4{1, 2, 3, 4})
215 So(vals[3].Value, ShouldResemble, dataSi ngleRoot[3].Value)
216
217 // inner, failing, transaction
218 So(ds.RunInTransaction(func(c context.Co ntext) error {
219 ds := datastore.Get(c)
220
221 // we can see stuff written in t he outer txn
222 So(7, fooShouldHave(ds))
223 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
224
225 So(3, fooSetTo(ds), 10, 20, 30, 40)
226
227 // disaster strikes!
228 return errors.New("whaaaa")
229 }, nil), ShouldErrLike, "whaaaa")
230
231 So(3, fooShouldHave(ds), 1, 2, 3, 4)
232
233 // inner, successful, transaction
234 So(ds.RunInTransaction(func(c context.Co ntext) error {
235 ds := datastore.Get(c)
236 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
237 So(3, fooSetTo(ds), 10, 20, 30, 40)
238 return nil
239 }, nil), ShouldBeNil)
240
241 // now we see it
242 So(3, fooShouldHave(ds), 10, 20, 30, 40)
243 return nil
244 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
245
246 // 2 because we are simulating a transaction fai lure
247 So(under.PutMulti.Total(), ShouldEqual, 2)
248 So(under.DeleteMulti.Total(), ShouldEqual, 2)
249
250 // 'over' Put operations are amplified because t he inner transaction
251 // commits go through the 'over' filter on the o uter transaction. So it's
252 // # Puts + # inner txns, times 2 because we are simulating a failed
253 // transaction.
254 So(over.PutMulti.Total(), ShouldEqual, 10)
255
256 So(7, fooShouldHave(ds))
257 So(3, fooShouldHave(ds), 10, 20, 30, 40)
258 })
259
260 Convey("can allocate IDs from an inner transaction", fun c() {
261 nums := []int64{4, 8, 15, 16, 23, 42}
262 k := (*datastore.Key)(nil)
263 So(ds.RunInTransaction(func(c context.Context) e rror {
264 ds := datastore.Get(c)
265
266 So(ds.RunInTransaction(func(c context.Co ntext) error {
267 ds := datastore.Get(c)
268 f := &Foo{Value: nums}
269 So(ds.Put(f), ShouldBeNil)
270 k = ds.KeyForObj(f)
271 return nil
272 }, nil), ShouldBeNil)
273
274 So(k.IntID(), fooShouldHave(ds), nums)
275
276 return nil
277 }, nil), ShouldBeNil)
278
279 So(k.IntID(), fooShouldHave(ds), nums)
280 })
281
282 })
283
284 Convey("Bad", func() {
285
286 Convey("too many roots", func() {
287 So(ds.RunInTransaction(func(c context.Context) e rror {
288 ds := datastore.Get(c)
289
290 f := &Foo{ID: 7}
291 So(ds.Get(f), ShouldBeNil)
292 So(f, ShouldResemble, dataMultiRoot[6])
293
294 So(ds.RunInTransaction(func(c context.Co ntext) error {
295 return datastore.Get(c).Get(&Foo {ID: 6})
296 }, nil), ShouldErrLike, "too many entity groups")
297
298 f.Value = []int64{9}
299 So(ds.Put(f), ShouldBeNil)
300
301 return nil
302 }, nil), ShouldBeNil)
303
304 f := &Foo{ID: 7}
305 So(ds.Get(f), ShouldBeNil)
306 So(f.Value, ShouldResemble, []int64{9})
307 })
308
309 Convey("buffered errors never reach the datastore", func () {
310 So(ds.RunInTransaction(func(c context.Context) e rror {
311 ds := datastore.Get(c)
312
313 So(ds.Put(&Foo{ID: 1, Value: []int64{1, 2, 3, 4}}), ShouldBeNil)
314 return errors.New("boop")
315 }, nil), ShouldErrLike, "boop")
316 So(under.PutMulti.Total(), ShouldEqual, 0)
317 So(over.PutMulti.Successes(), ShouldEqual, 1)
318 })
319
320 })
321
322 })
323 }
324
325 func TestHuge(t *testing.T) {
326 t.Parallel()
327
328 Convey("inner txn too big allows outer txn", t, func() {
329 _, _, ds := mkds(dataMultiRoot)
330
331 So(ds.RunInTransaction(func(c context.Context) error {
332 ds := datastore.Get(c)
333
334 So(18, fooSetTo(ds), hugeField)
335
336 So(ds.RunInTransaction(func(c context.Context) error {
337 ds := datastore.Get(c)
338 So(ds.PutMulti(hugeData), ShouldBeNil)
339 return nil
340 }, nil), ShouldErrLike, ErrTransactionTooLarge)
341
342 return nil
343 }, &datastore.TransactionOptions{XG: true}), ShouldBeNil)
344
345 So(18, fooShouldHave(ds), hugeField)
346 })
347
348 Convey("outer txn too big prevents inner txn", t, func() {
349 _, _, ds := mkds(dataMultiRoot)
350
351 So(ds.RunInTransaction(func(c context.Context) error {
352 ds := datastore.Get(c)
353
354 So(ds.PutMulti(hugeData), ShouldBeNil)
355
356 So(ds.RunInTransaction(func(c context.Context) error {
357 panic("never!")
358 }, nil), ShouldErrLike, ErrTransactionTooLarge)
359
360 return nil
361 }, &datastore.TransactionOptions{XG: true}), ShouldBeNil)
362
363 So(1, fooShouldHave(ds), hugeField)
364 })
365 }
366
367 func TestQuerySupport(t *testing.T) {
368 t.Parallel()
369
370 Convey("Queries", t, func() {
371 Convey("Good", func() {
372 q := datastore.NewQuery("Foo").Ancestor(root)
373
374 Convey("normal", func() {
375 _, _, ds := mkds(dataSingleRoot)
376 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
377 Kind: "Foo",
378 Ancestor: true,
379 SortBy: []datastore.IndexColumn{
380 {Property: "Value"},
381 },
382 })
383
384 So(ds.RunInTransaction(func(c context.Context) e rror {
385 ds := datastore.Get(c)
386
387 q = q.Lt("Value", 400000000000000000)
388
389 vals := []*Foo{}
390 So(ds.GetAll(q, &vals), ShouldBeNil)
391 So(len(vals), ShouldEqual, 8)
392
393 count, err := ds.Count(q)
394 So(err, ShouldBeNil)
395 So(count, ShouldEqual, 8)
396
397 f := &Foo{ID: 1, Parent: root}
398 So(ds.Get(f), ShouldBeNil)
399 f.Value = append(f.Value, 100)
400 So(ds.Put(f), ShouldBeNil)
401
402 // Wowee, zowee, merged queries!
403 vals2 := []*Foo{}
404 So(ds.GetAll(q, &vals2), ShouldBeNil)
405 So(len(vals2), ShouldEqual, 9)
406 So(vals2[0], ShouldResemble, f)
407
408 vals2 = []*Foo{}
409 So(ds.GetAll(q.Limit(2).Offset(1), &vals 2), ShouldBeNil)
410 So(len(vals2), ShouldEqual, 2)
411 So(vals2, ShouldResemble, vals[:2])
412
413 return nil
414 }, nil), ShouldBeNil)
415 })
416
417 Convey("keysOnly", func() {
418 _, _, ds := mkds([]*Foo{
419 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
420 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
421 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
422 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
423 })
424
425 So(ds.RunInTransaction(func(c context.Context) e rror {
426 ds := datastore.Get(c)
427
428 q = q.Eq("Value", 1).KeysOnly(true)
429 vals := []*datastore.Key{}
430 So(ds.GetAll(q, &vals), ShouldBeNil)
431 So(len(vals), ShouldEqual, 3)
432 So(vals[2], ShouldResemble, ds.MakeKey(" Parent", 1, "Foo", 5))
433
434 // can remove keys
435 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
436 vals = []*datastore.Key{}
437 So(ds.GetAll(q, &vals), ShouldBeNil)
438 So(len(vals), ShouldEqual, 2)
439
440 // and add new ones
441 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{1, 7, 100}}), ShouldBeNil)
442 So(ds.Put(&Foo{ID: 7, Parent: root, Valu e: []int64{20, 1}}), ShouldBeNil)
443 vals = []*datastore.Key{}
444 So(ds.GetAll(q, &vals), ShouldBeNil)
445 So(len(vals), ShouldEqual, 4)
446
447 So(vals[0].IntID(), ShouldEqual, 1)
448 So(vals[1].IntID(), ShouldEqual, 4)
449 So(vals[2].IntID(), ShouldEqual, 5)
450 So(vals[3].IntID(), ShouldEqual, 7)
451
452 return nil
453 }, nil), ShouldBeNil)
454 })
455
456 Convey("project", func() {
457 _, _, ds := mkds([]*Foo{
458 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
459 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
460 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
461 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
462 })
463
464 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
465 Kind: "Foo",
466 Ancestor: true,
467 SortBy: []datastore.IndexColumn{
468 {Property: "Value"},
469 },
470 })
471
472 So(ds.RunInTransaction(func(c context.Context) e rror {
473 ds := datastore.Get(c)
474
475 count, err := ds.Count(q.Project("Value" ))
476 So(err, ShouldBeNil)
477 So(count, ShouldEqual, 24)
478
479 q = q.Project("Value").Offset(4).Limit(1 0)
480
481 vals := []datastore.PropertyMap{}
482 So(ds.GetAll(q, &vals), ShouldBeNil)
483 So(len(vals), ShouldEqual, 10)
484
485 expect := []struct {
486 id int64
487 val int64
488 }{
489 {2, 3},
490 {3, 3},
491 {4, 3},
492 {2, 4},
493 {3, 4},
494 {2, 5},
495 {3, 5},
496 {4, 5},
497 {2, 6},
498 {3, 6},
499 }
500
501 for i, pm := range vals {
502 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
503 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
504 }
505
506 // should remove 4 entries, but there ar e plenty more to fill
507 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
508
509 vals = []datastore.PropertyMap{}
510 So(ds.GetAll(q, &vals), ShouldBeNil)
511 So(len(vals), ShouldEqual, 10)
512
513 expect = []struct {
514 id int64
515 val int64
516 }{
517 // note (3, 3) and (4, 3) are co rrectly missing because deleting
518 // 2 removed two entries which a re hidden by the Offset(4).
519 {3, 4},
520 {3, 5},
521 {4, 5},
522 {3, 6},
523 {3, 7},
524 {4, 7},
525 {3, 8},
526 {3, 9},
527 {4, 9},
528 {4, 11},
529 }
530
531 for i, pm := range vals {
532 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
533 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
534 }
535
536 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{3, 9}}), ShouldBeNil)
537
538 vals = []datastore.PropertyMap{}
539 So(ds.GetAll(q, &vals), ShouldBeNil)
540 So(len(vals), ShouldEqual, 10)
541
542 expect = []struct {
543 id int64
544 val int64
545 }{
546 // 'invisible' {1, 3} entry bump s the {4, 3} into view.
547 {4, 3},
548 {3, 4},
549 {3, 5},
550 {4, 5},
551 {3, 6},
552 {3, 7},
553 {4, 7},
554 {3, 8},
555 {1, 9},
556 {3, 9},
557 {4, 9},
558 }
559
560 for i, pm := range vals {
561 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
562 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
563 }
564
565 return nil
566 }, nil), ShouldBeNil)
567
568 })
569
570 Convey("project+distinct", func() {
571 _, _, ds := mkds([]*Foo{
572 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
573 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
574 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
575 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
576 })
577
578 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
579 Kind: "Foo",
580 Ancestor: true,
581 SortBy: []datastore.IndexColumn{
582 {Property: "Value"},
583 },
584 })
585
586 So(ds.RunInTransaction(func(c context.Context) e rror {
587 ds := datastore.Get(c)
588
589 q = q.Project("Value").Distinct(true)
590
591 vals := []datastore.PropertyMap{}
592 So(ds.GetAll(q, &vals), ShouldBeNil)
593 So(len(vals), ShouldEqual, 13)
594
595 expect := []struct {
596 id int64
597 val int64
598 }{
599 {2, 1},
600 {2, 2},
601 {2, 3},
602 {2, 4},
603 {2, 5},
604 {2, 6},
605 {2, 7},
606 {3, 8},
607 {3, 9},
608 {4, 11},
609 {5, 70},
610 {4, 100},
611 {5, 101},
612 }
613
614 for i, pm := range vals {
615 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
616 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
617 }
618
619 return nil
620 }, nil), ShouldBeNil)
621 })
622
623 Convey("overwrite", func() {
624 data := []*Foo{
625 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
626 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
627 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}},
628 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
629 }
630
631 _, _, ds := mkds(data)
632
633 q = q.Eq("Value", 2, 3)
634
635 So(ds.RunInTransaction(func(c context.Context) e rror {
636 ds := datastore.Get(c)
637
638 vals := []*Foo{}
639 So(ds.GetAll(q, &vals), ShouldBeNil)
640 So(len(vals), ShouldEqual, 2)
641
642 So(vals[0], ShouldResemble, data[0])
643 So(vals[1], ShouldResemble, data[2])
644
645 foo2 := &Foo{ID: 2, Parent: root, Value: []int64{2, 3}}
646 So(ds.Put(foo2), ShouldBeNil)
647
648 vals = []*Foo{}
649 So(ds.GetAll(q, &vals), ShouldBeNil)
650 So(len(vals), ShouldEqual, 2)
651
652 So(vals[0], ShouldResemble, foo2)
653 So(vals[1], ShouldResemble, data[2])
654
655 foo1 := &Foo{ID: 1, Parent: root, Value: []int64{2, 3}}
656 So(ds.Put(foo1), ShouldBeNil)
657
658 vals = []*Foo{}
659 So(ds.GetAll(q, &vals), ShouldBeNil)
660 So(len(vals), ShouldEqual, 3)
661
662 So(vals[0], ShouldResemble, foo1)
663 So(vals[1], ShouldResemble, foo2)
664 So(vals[2], ShouldResemble, data[2])
665
666 return nil
667 }, nil), ShouldBeNil)
668 })
669
670 projectData := []*Foo{
671 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}, Sort: []string{"x", "z"}},
672 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}, Sort: []string{"b"}},
673 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}, Sort: []string{"aa", "a"}},
674 {ID: 5, Parent: root, Value: []int64{1, 70, 101} , Sort: []string{"c"}},
675 }
676
677 Convey("project+extra orders", func() {
678
679 _, _, ds := mkds(projectData)
680 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
681 Kind: "Foo",
682 Ancestor: true,
683 SortBy: []datastore.IndexColumn{
684 {Property: "Sort", Descending: t rue},
685 {Property: "Value", Descending: true},
686 },
687 })
688
689 q = q.Project("Value").Order("-Sort", "-Value"). Distinct(true)
690 So(ds.RunInTransaction(func(c context.Context) e rror {
691 ds = datastore.Get(c)
692
693 So(ds.Put(&Foo{
694 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
695 Sort: []string{"zz"}}), ShouldBe Nil)
696
697 vals := []datastore.PropertyMap{}
698 So(ds.GetAll(q, &vals), ShouldBeNil)
699
700 expect := []struct {
701 id int64
702 val int64
703 }{
704 {1, 1000},
705 {1, 1},
706 {1, 0},
707 {2, 7},
708 {2, 6},
709 {2, 5},
710 {2, 4},
711 {2, 3},
712 {2, 2},
713 {5, 101},
714 {5, 70},
715 {3, 9},
716 {3, 8},
717 {4, 100},
718 {4, 11},
719 }
720
721 for i, pm := range vals {
722 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
723 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
724 }
725
726 return nil
727 }, nil), ShouldBeNil)
728 })
729
730 Convey("buffered entity sorts before ineq, but after fir st parent entity", func() {
731 // If we got this wrong, we'd see Foo,3 come bef ore Foo,2. This might
732 // happen because we calculate the comparison st ring for each entity
733 // based on the whole entity, but we forgot to l imit the comparison
734 // string generation by the inequality criteria.
735 data := []*Foo{
736 {ID: 2, Parent: root, Value: []int64{2, 3, 5, 6}, Sort: []string{"z"}},
737 }
738
739 _, _, ds := mkds(data)
740 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
741 Kind: "Foo",
742 Ancestor: true,
743 SortBy: []datastore.IndexColumn{
744 {Property: "Value"},
745 },
746 })
747
748 q = q.Gt("Value", 2).Limit(2)
749
750 So(ds.RunInTransaction(func(c context.Context) e rror {
751 ds = datastore.Get(c)
752
753 foo1 := &Foo{ID: 3, Parent: root, Value: []int64{0, 2, 3, 4}}
754 So(ds.Put(foo1), ShouldBeNil)
755
756 vals := []*Foo{}
757 So(ds.GetAll(q, &vals), ShouldBeNil)
758 So(len(vals), ShouldEqual, 2)
759
760 So(vals[0], ShouldResemble, data[0])
761 So(vals[1], ShouldResemble, foo1)
762
763 return nil
764 }, nil), ShouldBeNil)
765 })
766
767 Convey("keysOnly+extra orders", func() {
768 _, _, ds := mkds(projectData)
769 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
770 Kind: "Foo",
771 Ancestor: true,
772 SortBy: []datastore.IndexColumn{
773 {Property: "Sort"},
774 },
775 })
776
777 q = q.Order("Sort").KeysOnly(true)
778
779 So(ds.RunInTransaction(func(c context.Context) e rror {
780 ds = datastore.Get(c)
781
782 So(ds.Put(&Foo{
783 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
784 Sort: []string{"x", "zz"}}), Sho uldBeNil)
785
786 So(ds.Put(&Foo{
787 ID: 2, Parent: root, Value: []in t64{0, 1, 1000},
788 Sort: []string{"zz", "zzz", "zzz z"}}), ShouldBeNil)
789
790 vals := []*datastore.Key{}
791 So(ds.GetAll(q, &vals), ShouldBeNil)
792 So(len(vals), ShouldEqual, 5)
793
794 So(vals, ShouldResemble, []*datastore.Ke y{
795 ds.MakeKey("Parent", 1, "Foo", 4 ),
796 ds.MakeKey("Parent", 1, "Foo", 3 ),
797 ds.MakeKey("Parent", 1, "Foo", 5 ),
798 ds.MakeKey("Parent", 1, "Foo", 1 ),
799 ds.MakeKey("Parent", 1, "Foo", 2 ),
800 })
801
802 return nil
803 }, nil), ShouldBeNil)
804 })
805
806 Convey("query accross nested transactions", func() {
807 _, _, ds := mkds(projectData)
808 q = q.Eq("Value", 2, 3)
809
810 foo1 := &Foo{ID: 1, Parent: root, Value: []int64 {2, 3}}
811 foo7 := &Foo{ID: 7, Parent: root, Value: []int64 {2, 3}}
812
813 So(ds.RunInTransaction(func(c context.Context) e rror {
814 ds := datastore.Get(c)
815
816 So(ds.Put(foo1), ShouldBeNil)
817
818 vals := []*Foo{}
819 So(ds.GetAll(q, &vals), ShouldBeNil)
820 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], projectData[2]})
821
822 So(ds.RunInTransaction(func(c context.Co ntext) error {
823 ds := datastore.Get(c)
824
825 vals := []*Foo{}
826 So(ds.GetAll(q, &vals), ShouldBe Nil)
827 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], projectData[2]})
828
829 So(ds.Delete(ds.MakeKey("Parent" , 1, "Foo", 4)), ShouldBeNil)
830 So(ds.Put(foo7), ShouldBeNil)
831
832 vals = []*Foo{}
833 So(ds.GetAll(q, &vals), ShouldBe Nil)
834 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], foo7})
835
836 return nil
837 }, nil), ShouldBeNil)
838
839 vals = []*Foo{}
840 So(ds.GetAll(q, &vals), ShouldBeNil)
841 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], foo7})
842
843 return nil
844 }, nil), ShouldBeNil)
845
846 vals := []*Foo{}
847 So(ds.GetAll(q, &vals), ShouldBeNil)
848 So(vals, ShouldResemble, []*Foo{foo1, projectDat a[0], foo7})
849
850 })
851
852 Convey("start transaction from inside query", func() {
853 _, _, ds := mkds(projectData)
854 So(ds.RunInTransaction(func(c context.Context) e rror {
855 ds := datastore.Get(c)
856
857 q := datastore.NewQuery("Foo").Ancestor( root)
858 return ds.Run(q, func(pm datastore.Prope rtyMap, _ datastore.CursorCB) bool {
859 So(ds.RunInTransaction(func(c co ntext.Context) error {
860 pm["Value"] = append(pm[ "Value"], datastore.MkProperty("wat"))
861 return ds.Put(pm)
862 }, nil), ShouldBeNil)
863 return true
864 })
865 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
866
867 So(ds.Run(datastore.NewQuery("Foo"), func(pm dat astore.PropertyMap, _ datastore.CursorCB) bool {
868 val := pm["Value"]
869 So(val[len(val)-1].Value(), ShouldResemb le, "wat")
870 return true
871 }), ShouldBeNil)
872 })
873
874 })
875
876 })
877
878 }
OLDNEW
« no previous file with comments | « filter/txnBuf/state.go ('k') | impl/memory/datastore_data.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698