| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package gaeconfig | |
| 6 | |
| 7 import ( | |
| 8 "bytes" | |
| 9 "encoding/binary" | |
| 10 "fmt" | |
| 11 "time" | |
| 12 | |
| 13 ds "github.com/luci/gae/service/datastore" | |
| 14 mc "github.com/luci/gae/service/memcache" | |
| 15 "github.com/luci/luci-go/common/clock" | |
| 16 "github.com/luci/luci-go/common/config" | |
| 17 "github.com/luci/luci-go/common/config/filters/caching" | |
| 18 "github.com/luci/luci-go/common/data/caching/proccache" | |
| 19 log "github.com/luci/luci-go/common/logging" | |
| 20 | |
| 21 "golang.org/x/net/context" | |
| 22 ) | |
| 23 | |
| 24 // WrapWithCache wraps config client with proccache-and-memcache-caching layer. | |
| 25 func WrapWithCache(cfg config.Interface, expire time.Duration) config.Interface
{ | |
| 26 return caching.Wrap(cfg, caching.Options{ | |
| 27 Cache: &cache{}, | |
| 28 Expiration: expire, | |
| 29 }) | |
| 30 } | |
| 31 | |
| 32 type cache struct{} | |
| 33 | |
| 34 type proccacheKey string | |
| 35 | |
| 36 func (c *cache) Store(ctx context.Context, baseKey string, expire time.Duration,
value []byte) { | |
| 37 k := cacheKey(baseKey) | |
| 38 | |
| 39 proccache.Put(ctx, k, value, expire) | |
| 40 | |
| 41 // value in memcache is [varint(expiration_ts.Millis) ++ value] | |
| 42 // value in proccache is [value] | |
| 43 // | |
| 44 // This is because memcache doesn't populate the .Expiration field of th
e | |
| 45 // memcache Item on Get operations :( | |
| 46 stamp := ds.TimeToInt(clock.Now(ctx).UTC().Add(expire)) | |
| 47 buf := make([]byte, binary.MaxVarintLen64) | |
| 48 value = append(buf[:binary.PutVarint(buf, stamp)], value...) | |
| 49 | |
| 50 itm := mc.NewItem(ctx, string(k)).SetExpiration(expire).SetValue(value) | |
| 51 if err := mc.Set(ctx, itm); err != nil { | |
| 52 log.Fields{ | |
| 53 log.ErrorKey: err, | |
| 54 "key": baseKey, | |
| 55 "expire": expire, | |
| 56 }.Warningf(ctx, "Failed to store cache value.") | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 func (c *cache) Retrieve(ctx context.Context, baseKey string) []byte { | |
| 61 k := cacheKey(baseKey) | |
| 62 ret, err := proccache.GetOrMake(ctx, k, func() (value interface{}, exp t
ime.Duration, err error) { | |
| 63 item, err := mc.GetKey(ctx, string(k)) | |
| 64 if err != nil { | |
| 65 if err != mc.ErrCacheMiss { | |
| 66 log.Fields{ | |
| 67 log.ErrorKey: err, | |
| 68 "key": baseKey, | |
| 69 }.Warningf(ctx, "Failed to retrieve memcache val
ue.") | |
| 70 } | |
| 71 return | |
| 72 } | |
| 73 | |
| 74 buf := bytes.NewBuffer(item.Value()) | |
| 75 expStamp, err := binary.ReadVarint(buf) | |
| 76 if err != nil { | |
| 77 log.Fields{ | |
| 78 log.ErrorKey: err, | |
| 79 "key": baseKey, | |
| 80 }.Warningf(ctx, "Failed to decode stamp in memcache valu
e.") | |
| 81 return | |
| 82 } | |
| 83 | |
| 84 // proccache will ignore this value if exp is in the past | |
| 85 exp = ds.IntToTime(expStamp).Sub(clock.Now(ctx)) | |
| 86 value = buf.Bytes() | |
| 87 return | |
| 88 }) | |
| 89 if err != nil { | |
| 90 return nil | |
| 91 } | |
| 92 return ret.([]byte) | |
| 93 } | |
| 94 | |
| 95 func cacheKey(baseKey string) proccacheKey { | |
| 96 return proccacheKey(fmt.Sprintf("luci-config:v2:%s", baseKey)) | |
| 97 } | |
| OLD | NEW |