Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1006)

Side by Side Diff: filter/dscache/plan.go

Issue 1269113005: A transparent cache for datastore, backed by memcache. (Closed) Base URL: https://github.com/luci/gae.git@add_meta
Patch Set: fix comments Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « filter/dscache/globalconfig.go ('k') | filter/dscache/serialize.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « filter/dscache/globalconfig.go ('k') | filter/dscache/serialize.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698