go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/memory/datastore_index.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  	"bytes"
    19  	"fmt"
    20  	"sort"
    21  
    22  	"go.chromium.org/luci/common/data/cmpbin"
    23  
    24  	ds "go.chromium.org/luci/gae/service/datastore"
    25  )
    26  
    27  type qIndexSlice []*ds.IndexDefinition
    28  
    29  func (s qIndexSlice) Len() int           { return len(s) }
    30  func (s qIndexSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    31  func (s qIndexSlice) Less(i, j int) bool { return s[i].Less(s[j]) }
    32  
    33  func defaultIndexes(kind string, sip ds.IndexedProperties) []*ds.IndexDefinition {
    34  	ret := make(qIndexSlice, 0, 2*len(sip)+1)
    35  	ret = append(ret, &ds.IndexDefinition{Kind: kind})
    36  	for name := range sip {
    37  		// Skip top-level magical properties. But keep "nested_entity.__key__".
    38  		if name == "__key__" || name == "__ancestor__" {
    39  			continue
    40  		}
    41  		ret = append(ret, &ds.IndexDefinition{Kind: kind, SortBy: []ds.IndexColumn{{Property: name}}})
    42  		ret = append(ret, &ds.IndexDefinition{Kind: kind, SortBy: []ds.IndexColumn{{Property: name, Descending: true}}})
    43  	}
    44  	if serializationDeterministic {
    45  		sort.Sort(ret)
    46  	}
    47  	return ret
    48  }
    49  
    50  // indexEntriesWithBuiltins generates a new memStore containing the default
    51  // indexes for (k, pm) combined with complexIdxs.
    52  //
    53  // If "pm" is nil, this indicates an absence of a value. This is used
    54  // specifically for deletion.
    55  func indexEntriesWithBuiltins(k *ds.Key, pm ds.PropertyMap, complexIdxs []*ds.IndexDefinition) memStore {
    56  	var sip ds.IndexedProperties
    57  	if pm == nil {
    58  		return newMemStore()
    59  	}
    60  	sip = ds.Serialize.IndexedProperties(k, pm)
    61  	return indexEntries(k, sip, append(defaultIndexes(k.Kind(), sip), complexIdxs...))
    62  }
    63  
    64  // indexRowGen contains enough information to generate all of the index rows which
    65  // correspond with a propertyList and a ds.IndexDefinition.
    66  type indexRowGen struct {
    67  	propVec   []ds.IndexedPropertySlice
    68  	decending []bool
    69  }
    70  
    71  // permute calls cb for each index row, in some arbitrary order.
    72  func (s indexRowGen) permute(collSetFn func(k, v []byte)) {
    73  	iVec := make([]int, len(s.propVec))
    74  	iVecLim := make([]int, len(s.propVec))
    75  
    76  	incPos := func() bool {
    77  		for i := len(iVec) - 1; i >= 0; i-- {
    78  			var done bool
    79  			var newVal int
    80  			if !s.decending[i] {
    81  				newVal = (iVec[i] + 1) % iVecLim[i]
    82  				done = newVal != 0
    83  			} else {
    84  				newVal = (iVec[i] - 1)
    85  				if newVal < 0 {
    86  					newVal = iVecLim[i] - 1
    87  				} else {
    88  					done = true
    89  				}
    90  			}
    91  			iVec[i] = newVal
    92  			if done {
    93  				return true
    94  			}
    95  		}
    96  		return false
    97  	}
    98  
    99  	for i, sps := range s.propVec {
   100  		iVecLim[i] = len(sps)
   101  	}
   102  
   103  	for i := range iVec {
   104  		if s.decending[i] {
   105  			iVec[i] = iVecLim[i] - 1
   106  		}
   107  	}
   108  
   109  	for {
   110  		bufsiz := 0
   111  		for pvalSliceIdx, pvalIdx := range iVec {
   112  			bufsiz += len(s.propVec[pvalSliceIdx][pvalIdx])
   113  		}
   114  		buf := cmpbin.Invertible(bytes.NewBuffer(make([]byte, 0, bufsiz)))
   115  		for pvalSliceIdx, pvalIdx := range iVec {
   116  			data := s.propVec[pvalSliceIdx][pvalIdx]
   117  			buf.SetInvert(s.decending[pvalSliceIdx])
   118  			_, _ = buf.Write(data)
   119  		}
   120  		collSetFn(buf.Bytes(), []byte{})
   121  		if !incPos() {
   122  			break
   123  		}
   124  	}
   125  }
   126  
   127  type matcher struct {
   128  	buf indexRowGen
   129  }
   130  
   131  // matcher.match checks to see if the mapped, serialized property values
   132  // match the index. If they do, it returns a indexRowGen. Do not write or modify
   133  // the data in the indexRowGen.
   134  func (m *matcher) match(sortBy []ds.IndexColumn, sip ds.IndexedProperties) (indexRowGen, bool) {
   135  	m.buf.propVec = m.buf.propVec[:0]
   136  	m.buf.decending = m.buf.decending[:0]
   137  	for _, sb := range sortBy {
   138  		if pv, ok := sip[sb.Property]; ok {
   139  			m.buf.propVec = append(m.buf.propVec, pv)
   140  			m.buf.decending = append(m.buf.decending, sb.Descending)
   141  		} else {
   142  			return indexRowGen{}, false
   143  		}
   144  	}
   145  	return m.buf, true
   146  }
   147  
   148  // indexEntries generates a new memStore containing index entries for sip for
   149  // the supplied index definitions.
   150  func indexEntries(key *ds.Key, sip ds.IndexedProperties, idxs []*ds.IndexDefinition) memStore {
   151  	ret := newMemStore()
   152  	idxColl := ret.GetOrCreateCollection("idx")
   153  
   154  	mtch := matcher{}
   155  	for _, idx := range idxs {
   156  		idx = idx.Normalize()
   157  		if idx.Kind != "" && idx.Kind != key.Kind() {
   158  			continue
   159  		}
   160  		if irg, ok := mtch.match(idx.GetFullSortOrder(), sip); ok {
   161  			idxBin := ds.Serialize.ToBytes(*idx.PrepForIdxTable())
   162  			idxColl.Set(idxBin, []byte{})
   163  			coll := ret.GetOrCreateCollection(
   164  				fmt.Sprintf("idx:%s:%s", key.Namespace(), idxBin))
   165  			irg.permute(coll.Set)
   166  		}
   167  	}
   168  
   169  	return ret
   170  }
   171  
   172  // walkCompIdxs walks the table of compound indexes in the store. If `endsWith`
   173  // is provided, this will only walk over compound indexes which match
   174  // Kind, Ancestor, and whose SortBy has `endsWith.SortBy` as a suffix.
   175  func walkCompIdxs(store memStore, endsWith *ds.IndexDefinition, cb func(*ds.IndexDefinition) bool) {
   176  	idxColl := store.GetCollection("idx")
   177  	if idxColl == nil {
   178  		return
   179  	}
   180  	itrDef := iterDefinition{c: idxColl}
   181  
   182  	if endsWith != nil {
   183  		full := ds.Serialize.ToBytes(*endsWith.Flip())
   184  		// chop off the null terminating byte
   185  		itrDef.prefix = full[:len(full)-1]
   186  	}
   187  
   188  	it := itrDef.mkIter()
   189  	for ent := it.next(); ent != nil; ent = it.next() {
   190  		qi, err := ds.Deserialize.IndexDefinition(bytes.NewReader(ent.key))
   191  		memoryCorruption(err)
   192  		if !cb(qi.Flip()) {
   193  			break
   194  		}
   195  	}
   196  }
   197  
   198  func mergeIndexes(ns string, store, oldIdx, newIdx memStore) {
   199  	prefixBuf := []byte("idx:" + ns + ":")
   200  	origPrefixBufLen := len(prefixBuf)
   201  
   202  	oldIdx = oldIdx.Snapshot()
   203  	newIdx = newIdx.Snapshot()
   204  
   205  	memStoreCollide(oldIdx.GetCollection("idx"), newIdx.GetCollection("idx"), func(k, ov, nv []byte) {
   206  		prefixBuf = append(prefixBuf[:origPrefixBufLen], k...)
   207  		ks := string(prefixBuf)
   208  
   209  		coll := store.GetOrCreateCollection(ks)
   210  
   211  		oldColl := oldIdx.GetCollection(ks)
   212  		newColl := newIdx.GetCollection(ks)
   213  
   214  		switch {
   215  		case ov == nil && nv != nil: // all additions
   216  			newColl.ForEachItem(func(k, _ []byte) bool {
   217  				coll.Set(k, []byte{})
   218  				return true
   219  			})
   220  		case ov != nil && nv == nil: // all deletions
   221  			oldColl.ForEachItem(func(k, _ []byte) bool {
   222  				coll.Delete(k)
   223  				return true
   224  			})
   225  		case ov != nil && nv != nil: // merge
   226  			memStoreCollide(oldColl, newColl, func(k, ov, nv []byte) {
   227  				if nv == nil {
   228  					coll.Delete(k)
   229  				} else {
   230  					coll.Set(k, []byte{})
   231  				}
   232  			})
   233  		default:
   234  			impossible(fmt.Errorf("both values from memStoreCollide were nil?"))
   235  		}
   236  		// TODO(riannucci): remove entries from idxColl and remove index collections
   237  		// when there are no index entries for that index any more.
   238  	})
   239  }
   240  
   241  func addIndexes(store memStore, aid string, compIdx []*ds.IndexDefinition) {
   242  	normalized := make([]*ds.IndexDefinition, len(compIdx))
   243  	idxColl := store.GetOrCreateCollection("idx")
   244  	for i, idx := range compIdx {
   245  		normalized[i] = idx.Normalize()
   246  		idxColl.Set(ds.Serialize.ToBytes(*normalized[i].PrepForIdxTable()), []byte{})
   247  	}
   248  
   249  	for _, ns := range namespaces(store) {
   250  		kctx := ds.MkKeyContext(aid, ns)
   251  		if allEnts := store.Snapshot().GetCollection("ents:" + ns); allEnts != nil {
   252  			allEnts.ForEachItem(func(ik, iv []byte) bool {
   253  				pm, err := readPropMap(iv)
   254  				memoryCorruption(err)
   255  
   256  				prop, err := ds.Deserializer{KeyContext: kctx}.Property(bytes.NewBuffer(ik))
   257  				memoryCorruption(err)
   258  
   259  				k := prop.Value().(*ds.Key)
   260  
   261  				sip := ds.Serialize.IndexedProperties(k, pm)
   262  
   263  				mergeIndexes(ns, store,
   264  					newMemStore(),
   265  					indexEntries(k, sip, normalized))
   266  				return true
   267  			})
   268  		}
   269  	}
   270  }
   271  
   272  // updateIndexes updates the indexes in store to accommodate a change in entity
   273  // value.
   274  //
   275  // oldEnt is the previous entity value, and newEnt is the new entity value. If
   276  // newEnt is nil, that signifies deletion.
   277  func updateIndexes(store memStore, key *ds.Key, oldEnt, newEnt ds.PropertyMap) {
   278  	// load all current complex query index definitions.
   279  	var compIdx []*ds.IndexDefinition
   280  	walkCompIdxs(store.Snapshot(), nil, func(i *ds.IndexDefinition) bool {
   281  		compIdx = append(compIdx, i)
   282  		return true
   283  	})
   284  
   285  	mergeIndexes(key.Namespace(), store,
   286  		indexEntriesWithBuiltins(key, oldEnt, compIdx),
   287  		indexEntriesWithBuiltins(key, newEnt, compIdx))
   288  }