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 }