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, &currentTxnKey, 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 }