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

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

Issue 1269113005: A transparent cache for datastore, backed by memcache. (Closed) Base URL: https://github.com/luci/gae.git@add_meta
Patch Set: 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
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 "fmt"
10 "math/rand"
11 "time"
12
13 ds "github.com/luci/gae/service/datastore"
14 "github.com/luci/gae/service/memcache"
15 "github.com/luci/luci-go/common/errors"
16 "github.com/luci/luci-go/common/logging"
17 "golang.org/x/net/context"
18 )
19
20 type dsCache struct {
21 ds.RawInterface
22
23 aid string
24 ns string
25
26 mc memcache.Interface
27 log logging.Logger
28 mr *rand.Rand
29 }
30
31 var _ ds.RawInterface = (*dsCache)(nil)
32
33 func (d *dsCache) mutation(keys []ds.Key, f func() error) error {
34 lockItems, lockKeys := mkAllLockItems(d.mc, keys)
35 if lockItems == nil {
36 return f()
37 }
38 if err := d.mc.SetMulti(lockItems); err != nil {
39 d.log.Errorf("dscache: mc.SetMulti: %s", err)
40 }
41 err := f()
42 if err == nil {
43 if err := d.mc.DeleteMulti(lockKeys); err != nil {
44 d.log.Errorf("dscache: mc.DeleteMulti: %s", err)
45 }
46 }
47 return err
48 }
49
50 func (d *dsCache) DeleteMulti(keys []ds.Key, cb ds.DeleteMultiCB) error {
51 return d.mutation(keys, func() error {
52 return d.RawInterface.DeleteMulti(keys, cb)
53 })
54 }
55
56 func (d *dsCache) PutMulti(keys []ds.Key, vals []ds.PropertyMap, cb ds.PutMultiC B) error {
57 return d.mutation(keys, func() error {
58 return d.RawInterface.PutMulti(keys, vals, cb)
59 })
60 }
61
62 type facts struct {
63 getKeys []ds.Key
64 getMeta []ds.PropertyMap
65 lockItems []memcache.Item
66 }
67
68 type plan struct {
69 keepMeta bool
70
71 idxMap []int
72 toGet []ds.Key
73 toGetMeta []ds.PropertyMap
74 toSave []memcache.Item
75
76 decoded []ds.PropertyMap
77 lme errors.LazyMultiError
78 }
79
80 func (p *plan) add(idx int, get ds.Key, m ds.PropertyMap, save memcache.Item) {
81 p.idxMap = append(p.idxMap, idx)
82 p.toGet = append(p.toGet, get)
83
84 if save != nil {
85 save.SetFlags(uint32(ItemHasData))
86 }
87 p.toSave = append(p.toSave, save)
88
89 if p.keepMeta {
90 p.toGetMeta = append(p.toGetMeta, m)
91 }
92 }
93
94 func (p *plan) empty() bool {
95 return len(p.idxMap) == 0
96 }
97
98 func (d *dsCache) makePlan(f *facts) *plan {
99 // get index -> items index
100 p := plan{
101 keepMeta: f.getMeta != nil,
102 decoded: make([]ds.PropertyMap, len(f.lockItems)),
103 lme: errors.LazyMultiError{Size: len(f.lockItems)},
104 }
105 for i, lockItm := range f.lockItems {
106 curItm := f.lockItems[i]
107 m := ds.PropertyMap(nil)
108 if f.getMeta != nil {
109 m = f.getMeta[i]
110 }
111
112 getKey := f.getKeys[i]
113 if !meta(m, CacheEnableMeta, true).(bool) {
114 fmt.Println("cache is disabled!")
115 p.add(i, getKey, m, nil)
116 continue
117 }
118
119 if curItm == nil {
120 fmt.Println("missing item in memcache!")
121 p.add(i, getKey, m, nil)
122 continue
123 }
124
125 flg := FlagValue(curItm.Flags())
126 if flg == ItemHasLock {
127 if !bytes.Equal(curItm.Value(), lockItm.Value()) {
128 curItm = nil // someone else has the lock, don't save
129 }
130 fmt.Println("found lock item in memcache!")
131 p.add(i, getKey, m, curItm)
132 continue
133 }
134
135 pmap, err := decodeValue(curItm.Value(), d.aid, d.ns)
136 switch err {
137 case nil:
138 p.decoded[i] = pmap
139 case ds.ErrNoSuchEntity:
140 p.lme.Assign(i, ds.ErrNoSuchEntity)
141 default:
142 d.log.Errorf("dscache: error decoding %s, %s: %s", curIt m.Key(), getKey, err)
143 fmt.Println("failed to decode!")
144 p.add(i, getKey, m, curItm)
145 }
146 }
147 return &p
148 }
149
150 func (d *dsCache) GetMulti(keys []ds.Key, metas []ds.PropertyMap, cb ds.GetMulti CB) error {
151 lockItems := mkRandLockItems(d.mc, d.mr, keys)
152 if lockItems == nil {
153 return d.RawInterface.GetMulti(keys, metas, cb)
154 }
155
156 if err := d.mc.AddMulti(lockItems); err != nil {
157 d.log.Errorf("dscache: mc.AddMulti: %s", err)
158 }
159 if err := d.mc.GetMulti(lockItems); err != nil {
160 d.log.Errorf("dscache: mc.GetMulti: %s", err)
161 }
162
163 p := d.makePlan(&facts{keys, metas, lockItems})
164
165 if !p.empty() {
166 fmt.Println("have to touch DS :(", p)
167 toCas := []memcache.Item{}
168 j := 0
169 err := d.RawInterface.GetMulti(p.toGet, p.toGetMeta, func(pm ds. PropertyMap, err error) {
170 i := p.idxMap[j]
171 toSave := p.toSave[j]
172
173 data := []byte(nil)
174
175 if err == nil {
176 p.decoded[i] = pm
177 if toSave != nil {
178 data, err = mkItemData(pm)
179 }
180 }
181
182 canSave := true
183 if err != nil {
184 p.lme.Assign(i, err)
185 canSave = err == ds.ErrNoSuchEntity
186 }
187
188 if canSave && toSave != nil {
189 m := ds.PropertyMap(nil)
190 if p.toGetMeta != nil {
191 m = p.toGetMeta[j]
192 }
193 expSecs := meta(m, CacheExpirationMeta, CacheTim eSeconds).(int64)
194 toSave.SetExpiration(time.Duration(expSecs) * ti me.Second)
195 toSave.SetValue(data)
196 toCas = append(toCas, toSave)
197 }
198
199 j++
200 })
201 if err != nil {
202 return err
203 }
204 if len(toCas) > 0 {
205 if err := d.mc.CompareAndSwapMulti(toCas); err != nil {
206 d.log.Errorf("dscache: CompareAndSwapMulti: %s", err)
207 }
208 }
209 }
210
211 for i, dec := range p.decoded {
212 cb(dec, p.lme.GetOne(i))
213 }
214
215 return nil
216 }
217
218 func (d *dsCache) RunInTransaction(f func(context.Context) error, opts *ds.Trans actionOptions) error {
219 txnState := dsTxnState{}
220 err := d.RawInterface.RunInTransaction(func(ctx context.Context) error {
221 txnState.Reset()
222 err := f(context.WithValue(ctx, dsTxnCacheKey, &txnState))
223 if err != nil {
224 txnState.Apply(d.mc, d.log)
225 }
226 return err
227 }, opts)
228 if err == nil {
229 txnState.Release(d.mc, d.log)
230 }
231 return err
232 }
OLDNEW
« no previous file with comments | « filter/dscache/doc.go ('k') | filter/dscache/ds_txn.go » ('j') | filter/dscache/dscache_test.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698