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

Unified Diff: appengine/gaeconfig/cache.go

Issue 2575383002: Add server/cache support to gaeconfig. (Closed)
Patch Set: Created 4 years 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | appengine/gaeconfig/cache_test.go » ('j') | appengine/gaeconfig/default.go » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/gaeconfig/cache.go
diff --git a/appengine/gaeconfig/cache.go b/appengine/gaeconfig/cache.go
index 731aab93de00eac29de15c658ebd7f0ca2acd471..06743dcdc21300c2bc3f0509bfba372577a9c8e3 100644
--- a/appengine/gaeconfig/cache.go
+++ b/appengine/gaeconfig/cache.go
@@ -5,93 +5,90 @@
package gaeconfig
iannucci 2017/01/07 21:05:26 this package could be split into luci-config/cachi
dnj 2017/01/10 03:30:07 Sure, done.
import (
- "bytes"
- "encoding/binary"
- "fmt"
+ "encoding/hex"
"time"
- ds "github.com/luci/gae/service/datastore"
mc "github.com/luci/gae/service/memcache"
- "github.com/luci/luci-go/common/clock"
- "github.com/luci/luci-go/common/config"
- "github.com/luci/luci-go/common/config/filters/caching"
- "github.com/luci/luci-go/common/data/caching/proccache"
+ "github.com/luci/luci-go/common/errors"
log "github.com/luci/luci-go/common/logging"
+ "github.com/luci/luci-go/server/config"
+ "github.com/luci/luci-go/server/config/caching"
"golang.org/x/net/context"
)
-// WrapWithCache wraps config client with proccache-and-memcache-caching layer.
-func WrapWithCache(cfg config.Interface, expire time.Duration) config.Interface {
- return caching.Wrap(cfg, caching.Options{
- Cache: &cache{},
- Expiration: expire,
- })
-}
+const (
+ memCacheSchema = "v1"
+ maxMemCacheSize = 1024 * 1024 // 1MB
+)
-type cache struct{}
+func memcacheBackend(b config.Backend, exp time.Duration) config.Backend {
+ return &caching.Backend{
+ Backend: b,
+ CacheGet: func(c context.Context, key caching.Key, l caching.Loader) (*caching.Value, error) {
+ if key.Authority != config.AsService {
+ return l(c, key, nil)
+ }
-type proccacheKey string
+ // Is the item already cached?
+ k := memcacheKey(&key)
+ mci, err := mc.GetKey(c, k)
+ switch err {
+ case nil:
+ // Value was cached, successfully retrieved.
+ v, err := caching.DecodeValue(mci.Value())
+ if err != nil {
+ return nil, errors.Annotate(err).Reason("failed to decode cache value from %(key)q").
+ D("key", k).Err()
+ }
+ return v, nil
-func (c *cache) Store(ctx context.Context, baseKey string, expire time.Duration, value []byte) {
- k := cacheKey(baseKey)
+ case mc.ErrCacheMiss:
+ // Value was not cached. Load from Loader and cache.
+ v, err := l(c, key, nil)
+ if err != nil {
+ return nil, err
+ }
- proccache.Put(ctx, k, value, expire)
+ // Attempt to cache the value. If this fails, we'll log a warning and
+ // move on.
+ err = func() error {
+ d, err := v.Encode()
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to encode value").Err()
+ }
- // value in memcache is [varint(expiration_ts.Millis) ++ value]
- // value in proccache is [value]
- //
- // This is because memcache doesn't populate the .Expiration field of the
- // memcache Item on Get operations :(
- stamp := ds.TimeToInt(clock.Now(ctx).UTC().Add(expire))
- buf := make([]byte, binary.MaxVarintLen64)
- value = append(buf[:binary.PutVarint(buf, stamp)], value...)
+ if len(d) > maxMemCacheSize {
+ return errors.Reason("entry exceeds memcache size (%(size)d > %(max)d)").
iannucci 2017/01/07 21:05:26 should this be logged instead of an error?
dnj 2017/01/10 03:30:07 I don't think so. If config entries are really big
+ D("size", len(d)).D("max", maxMemCacheSize).Err()
+ }
- itm := mc.NewItem(ctx, string(k)).SetExpiration(expire).SetValue(value)
- if err := mc.Set(ctx, itm); err != nil {
- log.Fields{
- log.ErrorKey: err,
- "key": baseKey,
- "expire": expire,
- }.Warningf(ctx, "Failed to store cache value.")
- }
-}
+ item := mc.NewItem(c, k).SetValue(d).SetExpiration(exp)
+ if err := mc.Set(c, item); err != nil {
+ return errors.Annotate(err).Err()
+ }
+ return nil
+ }()
+ if err != nil {
+ log.Fields{
+ log.ErrorKey: err,
+ "key": k,
+ }.Warningf(c, "Failed to cache config.")
+ }
-func (c *cache) Retrieve(ctx context.Context, baseKey string) []byte {
- k := cacheKey(baseKey)
- ret, err := proccache.GetOrMake(ctx, k, func() (value interface{}, exp time.Duration, err error) {
- item, err := mc.GetKey(ctx, string(k))
- if err != nil {
- if err != mc.ErrCacheMiss {
+ // Return the loaded value.
+ return v, nil
+
+ default:
+ // Unknown memcache error.
log.Fields{
log.ErrorKey: err,
- "key": baseKey,
- }.Warningf(ctx, "Failed to retrieve memcache value.")
+ "key": k,
+ }.Warningf(c, "Failed to decode memcached config.")
+ return l(c, key, nil)
}
- return
- }
-
- buf := bytes.NewBuffer(item.Value())
- expStamp, err := binary.ReadVarint(buf)
- if err != nil {
- log.Fields{
- log.ErrorKey: err,
- "key": baseKey,
- }.Warningf(ctx, "Failed to decode stamp in memcache value.")
- return
- }
-
- // proccache will ignore this value if exp is in the past
- exp = ds.IntToTime(expStamp).Sub(clock.Now(ctx))
- value = buf.Bytes()
- return
- })
- if err != nil {
- return nil
+ },
}
- return ret.([]byte)
}
-func cacheKey(baseKey string) proccacheKey {
- return proccacheKey(fmt.Sprintf("luci-config:v2:%s", baseKey))
-}
+func memcacheKey(key *caching.Key) string { return hex.EncodeToString(key.ParamHash()) }
« no previous file with comments | « no previous file | appengine/gaeconfig/cache_test.go » ('j') | appengine/gaeconfig/default.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698