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

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

Issue 1309803004: Add transaction buffer filter. (Closed) Base URL: https://github.com/luci/gae.git@add_query_support
Patch Set: add err for too many roots 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 ds "github.com/luci/gae/service/datastore"
9 "github.com/luci/luci-go/common/errors"
10 "golang.org/x/net/context"
11 )
12
13 // ErrTransactionTooLarge is returned when applying an inner transaction would
14 // cause an outer transaction to become too large.
15 var ErrTransactionTooLarge = errors.New(
16 "applying the transaction would make the parent transaction too large")
17
18 // ErrTooManyRoots is returned when executing an operation which would cause
19 // the transaction to exceed it's alloted number of entity groups.
dnj 2015/09/30 16:35:26 nit: allotted**
iannucci 2015/09/30 17:10:27 done
20 var ErrTooManyRoots = errors.New(
21 "operating on too many entity groups in nested transaction")
22
23 type dsTxnBuf struct {
24 ic context.Context
25 state *txnBufState
26 }
27
28 var _ ds.RawInterface = (*dsTxnBuf)(nil)
29
30 func (d *dsTxnBuf) DecodeCursor(s string) (ds.Cursor, error) {
31 return d.state.parentDS.DecodeCursor(s)
32 }
33
34 func (d *dsTxnBuf) AllocateIDs(incomplete *ds.Key, n int) (start int64, err erro r) {
35 return d.state.parentDS.AllocateIDs(incomplete, n)
36 }
37
38 func (d *dsTxnBuf) GetMulti(keys []*ds.Key, metas ds.MultiMetaGetter, cb ds.GetM ultiCB) error {
39 data, err := d.state.getMulti(keys)
40 if err != nil {
41 return err
42 }
43
44 idxMap := []int(nil)
45 getKeys := []*ds.Key(nil)
46 getMetas := ds.MultiMetaGetter(nil)
47 lme := errors.NewLazyMultiError(len(keys))
48
49 for i, itm := range data {
50 if !itm.buffered {
51 idxMap = append(idxMap, i)
52 getKeys = append(getKeys, itm.key)
53 getMetas = append(getMetas, metas.GetSingle(i))
54 }
55 }
56
57 if len(idxMap) > 0 {
58 j := 0
59 err := d.state.parentDS.GetMulti(getKeys, getMetas, func(pm ds.P ropertyMap, err error) {
60 if err != ds.ErrNoSuchEntity {
61 i := idxMap[j]
62 if !lme.Assign(i, err) {
63 data[i].data = pm
64 }
65 }
66 j++
67 })
68 if err != nil {
69 return err
70 }
71 }
72
73 for i, itm := range data {
74 err := lme.GetOne(i)
75 if err != nil {
76 cb(nil, err)
77 } else if itm.data == nil {
78 cb(nil, ds.ErrNoSuchEntity)
79 } else {
80 cb(itm.data, nil)
81 }
82 }
83 return nil
84 }
85
86 func (d *dsTxnBuf) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.PutMult iCB) error {
87 lme := errors.NewLazyMultiError(len(keys))
88 realKeys := []*ds.Key(nil)
89 for i, key := range keys {
90 if key.Incomplete() {
91 start, err := d.AllocateIDs(key, 1)
92 if !lme.Assign(i, err) {
93 if realKeys == nil {
94 realKeys = make([]*ds.Key, len(keys))
95 copy(realKeys, keys)
96 }
97
98 aid, ns, toks := key.Split()
99 toks[len(toks)-1].IntID = start
100 realKeys[i] = ds.NewKeyToks(aid, ns, toks)
101 }
102 }
103 }
104 if err := lme.Get(); err != nil {
105 for _, e := range err.(errors.MultiError) {
106 if e == nil {
107 e = errors.New("putMulti failed because some key s were unable to AllocateIDs")
108 }
109 cb(nil, e)
110 }
111 return nil
112 }
113
114 if realKeys == nil {
115 realKeys = keys
116 }
117
118 err := d.state.putMulti(realKeys, vals)
119 if err != nil {
120 return err
121 }
122
123 for _, k := range realKeys {
124 cb(k, nil)
125 }
126 return nil
127 }
128
129 func (d *dsTxnBuf) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error {
130 err := d.state.deleteMulti(keys)
dnj 2015/09/30 16:35:26 nit: if err := ...; err != nil {...}
iannucci 2015/09/30 17:10:27 done
131 if err != nil {
132 return err
133 }
134
135 for range keys {
136 cb(nil)
137 }
138 return nil
139 }
140
141 func (d *dsTxnBuf) Count(fq *ds.FinalizedQuery) (count int64, err error) {
142 // Unfortunately there's no fast-path here. We literally have to run the
143 // query and count. Fortunately we can optimize to count keys if it's no t
144 // a projection query. This will save on bandwidth a bit.
145 if len(fq.Project()) == 0 && !fq.KeysOnly() {
146 fq, err = fq.Original().KeysOnly(true).Finalize()
147 if err != nil {
148 return
149 }
150 }
151 err = d.Run(fq, func(_ *ds.Key, _ ds.PropertyMap, _ ds.CursorCB) bool {
152 count++
153 return true
154 })
155 return
156 }
157
158 func (d *dsTxnBuf) Run(fq *ds.FinalizedQuery, cb ds.RawRunCB) error {
159 if start, end := fq.Bounds(); start != nil || end != nil {
160 return errors.New("txnBuf filter does not support query cursors" )
161 }
162
163 limit, limitSet := fq.Limit()
164 offset, _ := fq.Offset()
165 keysOnly := fq.KeysOnly()
166
167 project := fq.Project()
168
169 d.state.Lock()
170 memDS := d.state.memDS
171 parentDS := d.state.parentDS
172 sizes := d.state.entState.dup()
173 d.state.Unlock()
174
175 return runMergedQueries(fq, sizes, memDS, parentDS, func(key *ds.Key, da ta ds.PropertyMap) bool {
176 if offset > 0 {
177 offset--
178 return true
179 }
180 if limitSet {
181 if limit == 0 {
182 return false
183 }
184 limit--
185 }
186 if keysOnly {
187 data = nil
188 } else if len(project) > 0 {
189 newData := make(ds.PropertyMap, len(project))
190 for _, p := range project {
191 newData[p] = data[p]
dnj 2015/09/30 16:35:26 Is there any check to make sure the user doesn't p
iannucci 2015/09/30 17:10:27 projecting on `junk fields` (meaning fields that a
192 }
193 data = newData
194 }
195 return cb(key, data, nil)
196 })
197 }
198
199 func (d *dsTxnBuf) RunInTransaction(cb func(context.Context) error, opts *ds.Tra nsactionOptions) error {
200 return withTxnBuf(d.ic, cb, opts)
201 }
202
203 func (d *dsTxnBuf) Testable() ds.Testable {
204 return d.state.parentDS.Testable()
205 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698