go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/memory/datastore.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package memory 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 22 ds "go.chromium.org/luci/gae/service/datastore" 23 "go.chromium.org/luci/gae/service/info" 24 ) 25 26 //////////////////////////////////// public //////////////////////////////////// 27 28 // useRDS adds a gae.Datastore implementation to context, accessible 29 // by gae.GetDS(c) 30 func useRDS(c context.Context) context.Context { 31 return ds.SetRawFactory(c, func(ic context.Context) ds.RawInterface { 32 kc := ds.GetKeyContext(ic) 33 memCtx, isTxn := cur(ic) 34 dsd := memCtx.Get(memContextDSIdx) 35 if isTxn { 36 return &txnDsImpl{ic, dsd.(*txnDataStoreData), kc} 37 } 38 return &dsImpl{ic, dsd.(*dataStoreData), kc} 39 }) 40 } 41 42 // NewDatastore creates a new standalone memory implementation of the datastore, 43 // suitable for embedding for doing in-memory data organization. 44 // 45 // It's configured by default with the following settings: 46 // - AutoIndex(true) 47 // - Consistent(true) 48 // - DisableSpecialEntities(true) 49 // 50 // These settings can of course be changed by using the Testable interface. 51 func NewDatastore(c context.Context, inf info.RawInterface) ds.RawInterface { 52 kc := ds.GetKeyContext(c) 53 54 memctx := newMemContext(kc.AppID) 55 56 dsCtx := info.Set(context.Background(), inf) 57 rds := &dsImpl{dsCtx, memctx.Get(memContextDSIdx).(*dataStoreData), kc} 58 59 ret := ds.Raw(ds.SetRaw(dsCtx, rds)) 60 t := ret.GetTestable() 61 t.AutoIndex(true) 62 t.Consistent(true) 63 t.DisableSpecialEntities(true) 64 65 return ret 66 } 67 68 //////////////////////////////////// dsImpl //////////////////////////////////// 69 70 // dsImpl exists solely to bind the current c to the datastore data. 71 type dsImpl struct { 72 context.Context 73 74 data *dataStoreData 75 kc ds.KeyContext 76 } 77 78 var _ ds.RawInterface = (*dsImpl)(nil) 79 80 func (d *dsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { 81 return d.data.allocateIDs(keys, cb) 82 } 83 84 func (d *dsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKeyCB) error { 85 d.data.putMulti(keys, vals, cb, false) 86 return nil 87 } 88 89 func (d *dsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.GetMultiCB) error { 90 return d.data.getMulti(keys, cb) 91 } 92 93 func (d *dsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { 94 d.data.delMulti(keys, cb, false) 95 return nil 96 } 97 98 func (d *dsImpl) DecodeCursor(s string) (ds.Cursor, error) { 99 return newCursor(s) 100 } 101 102 func (d *dsImpl) Run(fq *ds.FinalizedQuery, cb ds.RawRunCB) error { 103 cb = d.data.stripSpecialPropsRunCB(cb) 104 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) 105 err := executeQuery(fq, d.kc, false, idx, head, cb) 106 if d.data.maybeAutoIndex(err) { 107 idx, head = d.data.getQuerySnaps(!fq.EventuallyConsistent()) 108 err = executeQuery(fq, d.kc, false, idx, head, cb) 109 } 110 return err 111 } 112 113 func (d *dsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { 114 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) 115 ret, err = countQuery(fq, d.kc, false, idx, head) 116 if d.data.maybeAutoIndex(err) { 117 idx, head := d.data.getQuerySnaps(!fq.EventuallyConsistent()) 118 ret, err = countQuery(fq, d.kc, false, idx, head) 119 } 120 return 121 } 122 123 func (d *dsImpl) WithoutTransaction() context.Context { 124 // Already not in a Transaction. 125 return d 126 } 127 128 func (*dsImpl) CurrentTransaction() ds.Transaction { return nil } 129 130 func (d *dsImpl) AddIndexes(idxs ...*ds.IndexDefinition) { 131 if len(idxs) == 0 { 132 return 133 } 134 135 for _, i := range idxs { 136 if !i.Compound() { 137 panic(fmt.Errorf("Attempted to add non-compound index: %s", i)) 138 } 139 } 140 141 d.data.addIndexes(idxs) 142 } 143 144 func (d *dsImpl) Constraints() ds.Constraints { return d.data.getConstraints() } 145 146 func (d *dsImpl) TakeIndexSnapshot() ds.TestingSnapshot { 147 return d.data.takeSnapshot() 148 } 149 150 func (d *dsImpl) SetIndexSnapshot(snap ds.TestingSnapshot) { 151 d.data.setSnapshot(snap.(memStore)) 152 } 153 154 func (d *dsImpl) CatchupIndexes() { 155 d.data.catchupIndexes() 156 } 157 158 func (d *dsImpl) SetTransactionRetryCount(count int) { 159 d.data.setTxnRetry(count) 160 } 161 162 func (d *dsImpl) Consistent(always bool) { 163 d.data.setConsistent(always) 164 } 165 166 func (d *dsImpl) AutoIndex(enable bool) { 167 d.data.setAutoIndex(enable) 168 } 169 170 func (d *dsImpl) DisableSpecialEntities(disabled bool) { 171 d.data.setDisableSpecialEntities(disabled) 172 } 173 174 func (d *dsImpl) ShowSpecialProperties(show bool) { 175 d.data.setShowSpecialProperties(show) 176 } 177 178 func (d *dsImpl) SetConstraints(c *ds.Constraints) error { 179 if c == nil { 180 c = &ds.Constraints{} 181 } 182 d.data.setConstraints(*c) 183 return nil 184 } 185 186 func (d *dsImpl) GetTestable() ds.Testable { return d } 187 188 ////////////////////////////////// txnDsImpl /////////////////////////////////// 189 190 type txnDsImpl struct { 191 context.Context 192 193 data *txnDataStoreData 194 kc ds.KeyContext 195 } 196 197 var _ ds.RawInterface = (*txnDsImpl)(nil) 198 199 func (d *txnDsImpl) AllocateIDs(keys []*ds.Key, cb ds.NewKeyCB) error { 200 return d.data.parent.allocateIDs(keys, cb) 201 } 202 203 func (d *txnDsImpl) PutMulti(keys []*ds.Key, vals []ds.PropertyMap, cb ds.NewKeyCB) error { 204 return d.data.run(func() error { 205 d.data.putMulti(keys, vals, cb) 206 return nil 207 }) 208 } 209 210 func (d *txnDsImpl) GetMulti(keys []*ds.Key, _meta ds.MultiMetaGetter, cb ds.GetMultiCB) error { 211 return d.data.run(func() error { 212 return d.data.getMulti(keys, cb) 213 }) 214 } 215 216 func (d *txnDsImpl) DeleteMulti(keys []*ds.Key, cb ds.DeleteMultiCB) error { 217 return d.data.run(func() error { 218 return d.data.delMulti(keys, cb) 219 }) 220 } 221 222 func (d *txnDsImpl) DecodeCursor(s string) (ds.Cursor, error) { return newCursor(s) } 223 224 func (d *txnDsImpl) Run(q *ds.FinalizedQuery, cb ds.RawRunCB) error { 225 // note that autoIndex has no effect inside transactions. This is because 226 // the transaction guarantees a consistent view of head at the time that the 227 // transaction opens. At best, we could add the index on head, but then return 228 // the error anyway, but adding the index then re-snapping at head would 229 // potentially reveal other entities not in the original transaction snapshot. 230 // 231 // It's possible that if you have full-consistency and also auto index enabled 232 // that this would make sense... but at that point you should probably just 233 // add the index up front. 234 cb = d.data.parent.stripSpecialPropsRunCB(cb) 235 return executeQuery(q, d.kc, true, d.data.snap, d.data.snap, cb) 236 } 237 238 func (d *txnDsImpl) Count(fq *ds.FinalizedQuery) (ret int64, err error) { 239 return countQuery(fq, d.kc, true, d.data.snap, d.data.snap) 240 } 241 242 func (*txnDsImpl) RunInTransaction(func(c context.Context) error, *ds.TransactionOptions) error { 243 return errors.New("datastore: nested transactions are not supported") 244 } 245 246 func (d *txnDsImpl) WithoutTransaction() context.Context { 247 return context.WithValue(d, ¤tTxnKey, nil) 248 } 249 250 func (d *txnDsImpl) CurrentTransaction() ds.Transaction { 251 return d.data.txn 252 } 253 254 func (d *txnDsImpl) Constraints() ds.Constraints { return d.data.parent.getConstraints() } 255 256 func (d *txnDsImpl) GetTestable() ds.Testable { return nil }