OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package dscache |
| 6 |
| 7 import ( |
| 8 "bytes" |
| 9 |
| 10 ds "github.com/luci/gae/service/datastore" |
| 11 mc "github.com/luci/gae/service/memcache" |
| 12 "github.com/luci/luci-go/common/errors" |
| 13 "github.com/luci/luci-go/common/logging" |
| 14 "golang.org/x/net/context" |
| 15 ) |
| 16 |
| 17 type facts struct { |
| 18 getKeys []ds.Key |
| 19 getMeta ds.MultiMetaGetter |
| 20 lockItems []mc.Item |
| 21 nonce []byte |
| 22 } |
| 23 |
| 24 type plan struct { |
| 25 keepMeta bool |
| 26 |
| 27 // idxMap maps from the original list of keys to the reduced set of keys
in |
| 28 // toGet. E.g. given the index `j` while looping over toGet, idxMap[j] w
ill |
| 29 // equal the index in the original facts.getKeys list. |
| 30 idxMap []int |
| 31 |
| 32 // toGet is a list of datstore keys to retrieve in the call down to the |
| 33 // underlying datastore. It will have length >= facts.getKeys. All the k
eys |
| 34 // in this will be valid (not nil). |
| 35 toGet []ds.Key |
| 36 |
| 37 // toGetMeta is a MultiMetaGetter to be passed in the call down to the |
| 38 // underlying datastore.GetMulti. It has the same length as toGet. |
| 39 toGetMeta ds.MultiMetaGetter |
| 40 |
| 41 // toSave is the list of memcache items to save the results from the |
| 42 // underlying datastore.GetMulti. It MAY contain nils, which is an indic
ator |
| 43 // that this entry SHOULD NOT be saved to memcache. |
| 44 toSave []mc.Item |
| 45 |
| 46 // decoded is a list of all the decoded property maps. Its length always
== |
| 47 // len(facts.getKeys). After the plan is formed, it may contain nils. Th
ese |
| 48 // nils will be filled in from the underlying datastore.GetMulti call in |
| 49 // ds.go. |
| 50 decoded []ds.PropertyMap |
| 51 |
| 52 // lme is a LazyMultiError whose target Size == len(facts.getKeys). The
errors |
| 53 // will eventually bubble back to the layer above this filter in callbac
ks. |
| 54 lme errors.LazyMultiError |
| 55 } |
| 56 |
| 57 // add adds a new entry to be retrieved from the actual underlying datastore |
| 58 // (via GetMulti). |
| 59 // |
| 60 // - idx is the index into the original facts.getKeys |
| 61 // - get and m are the pair of values that will be passed to datastore.GetMult
i |
| 62 // - save is the memcache item to save the result back to. If it's nil, then |
| 63 // it will not be saved back to memcache. |
| 64 func (p *plan) add(idx int, get ds.Key, m ds.MetaGetter, save mc.Item) { |
| 65 p.idxMap = append(p.idxMap, idx) |
| 66 p.toGet = append(p.toGet, get) |
| 67 |
| 68 p.toSave = append(p.toSave, save) |
| 69 |
| 70 if p.keepMeta { |
| 71 p.toGetMeta = append(p.toGetMeta, m) |
| 72 } |
| 73 } |
| 74 |
| 75 func (p *plan) empty() bool { |
| 76 return len(p.idxMap) == 0 |
| 77 } |
| 78 |
| 79 // makeFetchPlan takes the input facts and makes a plan about what to do with th
em. |
| 80 // |
| 81 // Possible scenarios: |
| 82 // * all entries we got from memcache are valid data, and so we don't need |
| 83 // to call through to the underlying datastore at all. |
| 84 // * some entries are 'lock' entries, owned by us, and so we should get them |
| 85 // from datastore and then attempt to save them back to memcache. |
| 86 // * some entries are 'lock' entries, owned by something else, so we should |
| 87 // get them from datastore and then NOT save them to memcache. |
| 88 // |
| 89 // Or some combination thereof. This also handles memcache enries with invalid |
| 90 // data in them, cases where items have caching disabled entirely, etc. |
| 91 func makeFetchPlan(c context.Context, aid, ns string, f *facts) *plan { |
| 92 p := plan{ |
| 93 keepMeta: f.getMeta != nil, |
| 94 decoded: make([]ds.PropertyMap, len(f.lockItems)), |
| 95 lme: errors.LazyMultiError{Size: len(f.lockItems)}, |
| 96 } |
| 97 for i, lockItm := range f.lockItems { |
| 98 m := f.getMeta.GetSingle(i) |
| 99 getKey := f.getKeys[i] |
| 100 |
| 101 if lockItm == nil { |
| 102 // this item wasn't cacheable (e.g. the model had cachin
g disabled, |
| 103 // shardsForKey returned 0, etc.) |
| 104 p.add(i, getKey, m, nil) |
| 105 continue |
| 106 } |
| 107 |
| 108 switch FlagValue(lockItm.Flags()) { |
| 109 case ItemHasLock: |
| 110 if bytes.Equal(f.nonce, lockItm.Value()) { |
| 111 // we have the lock |
| 112 p.add(i, getKey, m, lockItm) |
| 113 } else { |
| 114 // someone else has the lock, don't save |
| 115 p.add(i, getKey, m, nil) |
| 116 } |
| 117 |
| 118 case ItemHasData: |
| 119 pmap, err := decodeItemValue(lockItm.Value(), aid, ns) |
| 120 switch err { |
| 121 case nil: |
| 122 p.decoded[i] = pmap |
| 123 case ds.ErrNoSuchEntity: |
| 124 p.lme.Assign(i, ds.ErrNoSuchEntity) |
| 125 default: |
| 126 (logging.Fields{"error": err}).Warningf(c, |
| 127 "dscache: error decoding %s, %s", lockIt
m.Key(), getKey) |
| 128 p.add(i, getKey, m, nil) |
| 129 } |
| 130 |
| 131 default: |
| 132 // have some other sort of object, or our AddMulti faile
d to add this item. |
| 133 p.add(i, getKey, m, nil) |
| 134 } |
| 135 } |
| 136 return &p |
| 137 } |
OLD | NEW |