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

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

Powered by Google App Engine
This is Rietveld 408576698