OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 | 10 |
11 "golang.org/x/net/context" | 11 "golang.org/x/net/context" |
12 | 12 |
13 ds "github.com/luci/gae/service/datastore" | 13 ds "github.com/luci/gae/service/datastore" |
14 "github.com/luci/gae/service/info" | 14 "github.com/luci/gae/service/info" |
15 ) | 15 ) |
16 | 16 |
17 //////////////////////////////////// public //////////////////////////////////// | 17 //////////////////////////////////// public //////////////////////////////////// |
18 | 18 |
19 // useRDS adds a gae.Datastore implementation to context, accessible | 19 // useRDS adds a gae.Datastore implementation to context, accessible |
20 // by gae.GetDS(c) | 20 // by gae.GetDS(c) |
21 func useRDS(c context.Context) context.Context { | 21 func useRDS(c context.Context) context.Context { |
22 » return ds.SetRawFactory(c, func(ic context.Context, wantTxn bool) ds.Raw
Interface { | 22 » return ds.SetRawFactory(c, func(ic context.Context) ds.RawInterface { |
23 » » ns, hasNS := curGID(ic).getNamespace() | 23 » » kc := ds.GetKeyContext(ic) |
24 » » maybeTxnCtx := cur(ic) | 24 » » memCtx, isTxn := cur(ic) |
25 | 25 » » dsd := memCtx.Get(memContextDSIdx) |
26 » » needResetCtx := false | 26 » » if isTxn { |
27 » » if !wantTxn { | 27 » » » return &txnDsImpl{ic, dsd.(*txnDataStoreData), kc} |
28 » » » rootctx := curNoTxn(ic) | |
29 » » » if rootctx != maybeTxnCtx { | |
30 » » » » needResetCtx = true | |
31 » » » » maybeTxnCtx = rootctx | |
32 » » » } | |
33 } | 28 } |
34 | 29 » » return &dsImpl{ic, dsd.(*dataStoreData), kc} |
35 » » dsd := maybeTxnCtx.Get(memContextDSIdx) | |
36 » » if x, ok := dsd.(*dataStoreData); ok { | |
37 » » » if needResetCtx { | |
38 » » » » ic = context.WithValue(ic, memContextKey, maybeT
xnCtx) | |
39 » » » } | |
40 » » » return &dsImpl{x, ns, hasNS, ic} | |
41 » » } | |
42 » » return &txnDsImpl{dsd.(*txnDataStoreData), ns, hasNS} | |
43 }) | 30 }) |
44 } | 31 } |
45 | 32 |
46 // NewDatastore creates a new standalone memory implementation of the datastore, | 33 // NewDatastore creates a new standalone memory implementation of the datastore, |
47 // suitable for embedding for doing in-memory data organization. | 34 // suitable for embedding for doing in-memory data organization. |
48 // | 35 // |
49 // It's configured by default with the following settings: | 36 // It's configured by default with the following settings: |
50 // * AutoIndex(true) | 37 // * AutoIndex(true) |
51 // * Consistent(true) | 38 // * Consistent(true) |
52 // * DisableSpecialEntities(true) | 39 // * DisableSpecialEntities(true) |
53 // | 40 // |
54 // These settings can of course be changed by using the Testable() interface. | 41 // These settings can of course be changed by using the Testable interface. |
55 func NewDatastore(inf info.Interface) ds.Interface { | 42 func NewDatastore(c context.Context, inf info.RawInterface) ds.RawInterface { |
56 » fqAppID := inf.FullyQualifiedAppID() | 43 » kc := ds.GetKeyContext(c) |
57 » ns, hasNS := inf.GetNamespace() | |
58 | 44 |
59 » memctx := newMemContext(fqAppID) | 45 » memctx := newMemContext(kc.AppID) |
60 | 46 |
61 dsCtx := info.Set(context.Background(), inf) | 47 dsCtx := info.Set(context.Background(), inf) |
62 » rds := &dsImpl{memctx.Get(memContextDSIdx).(*dataStoreData), ns, hasNS,
dsCtx} | 48 » rds := &dsImpl{dsCtx, memctx.Get(memContextDSIdx).(*dataStoreData), kc} |
63 | 49 |
64 » ret := ds.Get(ds.SetRaw(dsCtx, rds)) | 50 » ret := ds.Raw(ds.SetRaw(dsCtx, rds)) |
65 » t := ret.Testable() | 51 » t := ret.GetTestable() |
66 t.AutoIndex(true) | 52 t.AutoIndex(true) |
67 t.Consistent(true) | 53 t.Consistent(true) |
68 t.DisableSpecialEntities(true) | 54 t.DisableSpecialEntities(true) |
69 | 55 |
70 return ret | 56 return ret |
71 } | 57 } |
72 | 58 |
73 //////////////////////////////////// dsImpl //////////////////////////////////// | 59 //////////////////////////////////// dsImpl //////////////////////////////////// |
74 | 60 |
75 // dsImpl exists solely to bind the current c to the datastore data. | 61 // dsImpl exists solely to bind the current c to the datastore data. |
76 type dsImpl struct { | 62 type dsImpl struct { |
77 » data *dataStoreData | 63 » context.Context |
78 » ns string | 64 |
79 » hasNS bool | 65 » data *dataStoreData |
80 » c context.Context | 66 » kc ds.KeyContext |
81 } | 67 } |
82 | 68 |
83 var _ ds.RawInterface = (*dsImpl)(nil) | 69 var _ ds.RawInterface = (*dsImpl)(nil) |
84 | 70 |
85 func (d *dsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { | 71 func (d *dsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { |
86 return d.data.allocateIDs(keys, cb) | 72 return d.data.allocateIDs(keys, cb) |
87 } | 73 } |
88 | 74 |
89 func (d *dsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKeyCB)
error { | 75 func (d *dsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKeyCB)
error { |
90 d.data.putMulti(keys, vals, cb) | 76 d.data.putMulti(keys, vals, cb) |
91 return nil | 77 return nil |
92 } | 78 } |
93 | 79 |
94 func (d *dsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.GetMul
tiCB) error { | 80 func (d *dsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.GetMul
tiCB) error { |
95 return d.data.getMulti(keys, cb) | 81 return d.data.getMulti(keys, cb) |
96 } | 82 } |
97 | 83 |
98 func (d *dsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { | 84 func (d *dsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { |
99 d.data.delMulti(keys, cb) | 85 d.data.delMulti(keys, cb) |
100 return nil | 86 return nil |
101 } | 87 } |
102 | 88 |
103 func (d *dsImpl) DecodeCursor(s string) (ds.Cursor, error) { | 89 func (d *dsImpl) DecodeCursor(s string) (ds.Cursor, error) { |
104 return newCursor(s) | 90 return newCursor(s) |
105 } | 91 } |
106 | 92 |
107 func (d *dsImpl) Run(fq *ds.FinalizedQuery, cb ds.RawRunCB) error { | 93 func (d *dsImpl) Run(fq *ds.FinalizedQuery, cb ds.RawRunCB) error { |
108 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) | 94 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) |
109 » err := executeQuery(fq, d.data.aid, d.ns, false, idx, head, cb) | 95 » err := executeQuery(fq, d.kc, false, idx, head, cb) |
110 if d.data.maybeAutoIndex(err) { | 96 if d.data.maybeAutoIndex(err) { |
111 idx, head = d.data.getQuerySnaps(!fq.EventuallyConsistent()) | 97 idx, head = d.data.getQuerySnaps(!fq.EventuallyConsistent()) |
112 » » err = executeQuery(fq, d.data.aid, d.ns, false, idx, head, cb) | 98 » » err = executeQuery(fq, d.kc, false, idx, head, cb) |
113 } | 99 } |
114 return err | 100 return err |
115 } | 101 } |
116 | 102 |
117 func (d *dsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { | 103 func (d *dsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { |
118 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) | 104 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) |
119 » ret, err = countQuery(fq, d.data.aid, d.ns, false, idx, head) | 105 » ret, err = countQuery(fq, d.kc, false, idx, head) |
120 if d.data.maybeAutoIndex(err) { | 106 if d.data.maybeAutoIndex(err) { |
121 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) | 107 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) |
122 » » ret, err = countQuery(fq, d.data.aid, d.ns, false, idx, head) | 108 » » ret, err = countQuery(fq, d.kc, false, idx, head) |
123 } | 109 } |
124 return | 110 return |
125 } | 111 } |
126 | 112 |
| 113 func (d *dsImpl) WithTransaction(t ds.Transaction) context.Context { |
| 114 if t == nil { |
| 115 // Already not in a Transaction. |
| 116 return d |
| 117 } |
| 118 panic("cannot manually set transaction") |
| 119 } |
| 120 |
| 121 func (*dsImpl) CurrentTransaction() ds.Transaction { return nil } |
| 122 |
127 func (d *dsImpl) AddIndexes(idxs ...*ds.IndexDefinition) { | 123 func (d *dsImpl) AddIndexes(idxs ...*ds.IndexDefinition) { |
128 if len(idxs) == 0 { | 124 if len(idxs) == 0 { |
129 return | 125 return |
130 } | 126 } |
131 | 127 |
132 for _, i := range idxs { | 128 for _, i := range idxs { |
133 if !i.Compound() { | 129 if !i.Compound() { |
134 panic(fmt.Errorf("Attempted to add non-compound index: %
s", i)) | 130 panic(fmt.Errorf("Attempted to add non-compound index: %
s", i)) |
135 } | 131 } |
136 } | 132 } |
(...skipping 22 matching lines...) Expand all Loading... |
159 } | 155 } |
160 | 156 |
161 func (d *dsImpl) AutoIndex(enable bool) { | 157 func (d *dsImpl) AutoIndex(enable bool) { |
162 d.data.setAutoIndex(enable) | 158 d.data.setAutoIndex(enable) |
163 } | 159 } |
164 | 160 |
165 func (d *dsImpl) DisableSpecialEntities(enabled bool) { | 161 func (d *dsImpl) DisableSpecialEntities(enabled bool) { |
166 d.data.setDisableSpecialEntities(enabled) | 162 d.data.setDisableSpecialEntities(enabled) |
167 } | 163 } |
168 | 164 |
169 func (d *dsImpl) Testable() ds.Testable { | 165 func (d *dsImpl) GetTestable() ds.Testable { return d } |
170 » return d | |
171 } | |
172 | 166 |
173 ////////////////////////////////// txnDsImpl /////////////////////////////////// | 167 ////////////////////////////////// txnDsImpl /////////////////////////////////// |
174 | 168 |
175 type txnDsImpl struct { | 169 type txnDsImpl struct { |
176 » data *txnDataStoreData | 170 » context.Context |
177 » ns string | 171 |
178 » hasNS bool | 172 » data *txnDataStoreData |
| 173 » kc ds.KeyContext |
179 } | 174 } |
180 | 175 |
181 var _ ds.RawInterface = (*txnDsImpl)(nil) | 176 var _ ds.RawInterface = (*txnDsImpl)(nil) |
182 | 177 |
183 func (d *txnDsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { | 178 func (d *txnDsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { |
184 return d.data.parent.allocateIDs(keys, cb) | 179 return d.data.parent.allocateIDs(keys, cb) |
185 } | 180 } |
186 | 181 |
187 func (d *txnDsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKey
CB) error { | 182 func (d *txnDsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKey
CB) error { |
188 return d.data.run(func() error { | 183 return d.data.run(func() error { |
189 d.data.putMulti(keys, vals, cb) | 184 d.data.putMulti(keys, vals, cb) |
190 return nil | 185 return nil |
191 }) | 186 }) |
192 } | 187 } |
193 | 188 |
194 func (d *txnDsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.Get
MultiCB) error { | 189 func (d *txnDsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.Get
MultiCB) error { |
195 return d.data.run(func() error { | 190 return d.data.run(func() error { |
196 return d.data.getMulti(keys, cb) | 191 return d.data.getMulti(keys, cb) |
197 }) | 192 }) |
198 } | 193 } |
199 | 194 |
200 func (d *txnDsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { | 195 func (d *txnDsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { |
201 return d.data.run(func() error { | 196 return d.data.run(func() error { |
202 return d.data.delMulti(keys, cb) | 197 return d.data.delMulti(keys, cb) |
203 }) | 198 }) |
204 } | 199 } |
205 | 200 |
206 func (d *txnDsImpl) DecodeCursor(s string) (ds.Cursor, error) { | 201 func (d *txnDsImpl) DecodeCursor(s string) (ds.Cursor, error) { return newCursor
(s) } |
207 » return newCursor(s) | |
208 } | |
209 | 202 |
210 func (d *txnDsImpl) Run(q *ds.FinalizedQuery, cb ds.RawRunCB) error { | 203 func (d *txnDsImpl) Run(q *ds.FinalizedQuery, cb ds.RawRunCB) error { |
211 // note that autoIndex has no effect inside transactions. This is becaus
e | 204 // note that autoIndex has no effect inside transactions. This is becaus
e |
212 // the transaction guarantees a consistent view of head at the time that
the | 205 // the transaction guarantees a consistent view of head at the time that
the |
213 // transaction opens. At best, we could add the index on head, but then
return | 206 // transaction opens. At best, we could add the index on head, but then
return |
214 // the error anyway, but adding the index then re-snapping at head would | 207 // the error anyway, but adding the index then re-snapping at head would |
215 // potentially reveal other entities not in the original transaction sna
pshot. | 208 // potentially reveal other entities not in the original transaction sna
pshot. |
216 // | 209 // |
217 // It's possible that if you have full-consistency and also auto index e
nabled | 210 // It's possible that if you have full-consistency and also auto index e
nabled |
218 // that this would make sense... but at that point you should probably j
ust | 211 // that this would make sense... but at that point you should probably j
ust |
219 // add the index up front. | 212 // add the index up front. |
220 » return executeQuery(q, d.data.parent.aid, d.ns, true, d.data.snap, d.dat
a.snap, cb) | 213 » return executeQuery(q, d.kc, true, d.data.snap, d.data.snap, cb) |
221 } | 214 } |
222 | 215 |
223 func (d *txnDsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { | 216 func (d *txnDsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { |
224 » return countQuery(fq, d.data.parent.aid, d.ns, true, d.data.snap, d.data
.snap) | 217 » return countQuery(fq, d.kc, true, d.data.snap, d.data.snap) |
225 } | 218 } |
226 | 219 |
227 func (*txnDsImpl) RunInTransaction(func(c context.Context) error, *ds.Transactio
nOptions) error { | 220 func (*txnDsImpl) RunInTransaction(func(c context.Context) error, *ds.Transactio
nOptions) error { |
228 return errors.New("datastore: nested transactions are not supported") | 221 return errors.New("datastore: nested transactions are not supported") |
229 } | 222 } |
230 | 223 |
231 func (*txnDsImpl) Testable() ds.Testable { | 224 func (d *txnDsImpl) WithTransaction(t ds.Transaction) context.Context { |
232 » return nil | 225 » if t != nil { |
| 226 » » panic("cannot manually set transaction") |
| 227 » } |
| 228 » return context.WithValue(d, currentTxnKey, nil) |
233 } | 229 } |
| 230 |
| 231 func (d *txnDsImpl) CurrentTransaction() ds.Transaction { |
| 232 return d.data.txn |
| 233 } |
| 234 |
| 235 func (d *txnDsImpl) GetTestable() ds.Testable { return nil } |
OLD | NEW |