| Index: filter/dscache/support.go
|
| diff --git a/filter/dscache/support.go b/filter/dscache/support.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f57a9e9df2408c3dfca4353daffdcd043652a0d2
|
| --- /dev/null
|
| +++ b/filter/dscache/support.go
|
| @@ -0,0 +1,158 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package dscache
|
| +
|
| +import (
|
| + "fmt"
|
| + "math/rand"
|
| + "time"
|
| +
|
| + ds "github.com/luci/gae/service/datastore"
|
| + "github.com/luci/gae/service/memcache"
|
| + log "github.com/luci/luci-go/common/logging"
|
| + "golang.org/x/net/context"
|
| +)
|
| +
|
| +type supportContext struct {
|
| + aid string
|
| + ns string
|
| +
|
| + c context.Context
|
| + mc memcache.Interface
|
| + mr *rand.Rand
|
| + shardsForKey func(ds.Key) int
|
| +}
|
| +
|
| +func (s *supportContext) numShards(k ds.Key) int {
|
| + ret := DefaultShards
|
| + if s.shardsForKey != nil {
|
| + ret = s.shardsForKey(k)
|
| + }
|
| + if ret < 1 {
|
| + return 0 // disable caching entirely
|
| + }
|
| + if ret > MaxShards {
|
| + ret = MaxShards
|
| + }
|
| + return ret
|
| +}
|
| +
|
| +func (s *supportContext) mkRandKeys(keys []ds.Key, metas ds.MultiMetaGetter) []string {
|
| + ret := []string(nil)
|
| + for i, key := range keys {
|
| + if !metas.GetMetaDefault(i, CacheEnableMeta, true).(bool) {
|
| + continue
|
| + }
|
| + shards := s.numShards(key)
|
| + if shards == 0 {
|
| + continue
|
| + }
|
| + if ret == nil {
|
| + ret = make([]string, len(keys))
|
| + }
|
| + ret[i] = MakeMemcacheKey(s.mr.Intn(shards), key)
|
| + }
|
| + return ret
|
| +}
|
| +
|
| +func (s *supportContext) mkAllKeys(keys []ds.Key) []string {
|
| + size := 0
|
| + nums := make([]int, len(keys))
|
| + for i, k := range keys {
|
| + if !ds.KeyIncomplete(k) {
|
| + shards := s.numShards(k)
|
| + nums[i] = shards
|
| + size += shards
|
| + }
|
| + }
|
| + if size == 0 {
|
| + return nil
|
| + }
|
| + ret := make([]string, 0, size)
|
| + for i, key := range keys {
|
| + if !ds.KeyIncomplete(key) {
|
| + keySuffix := HashKey(key)
|
| + for shard := 0; shard < nums[i]; shard++ {
|
| + ret = append(ret, fmt.Sprintf(KeyFormat, shard, keySuffix))
|
| + }
|
| + }
|
| + }
|
| + return ret
|
| +}
|
| +
|
| +// crappyNonce creates a really crappy nonce using math/rand. This is generally
|
| +// unacceptable for cryptographic purposes, but since mathrand is the only
|
| +// mocked randomness source, we use that.
|
| +//
|
| +// The random values here are controlled entriely by the application, will never
|
| +// be shown to, or provided by, the user, so this should be fine.
|
| +//
|
| +// Do not use this function for anything other than mkRandLockItems or your hair
|
| +// will fall out. You've been warned.
|
| +func (s *supportContext) crappyNonce() []byte {
|
| + ret := make([]byte, NonceUint32s*4)
|
| + for w := uint(0); w < NonceUint32s; w++ {
|
| + word := s.mr.Uint32()
|
| + for i := uint(0); i < 4; i++ {
|
| + ret[(w*4)+i] = byte(word >> (8 * i))
|
| + }
|
| + }
|
| + return ret
|
| +}
|
| +
|
| +func (s *supportContext) mutation(keys []ds.Key, f func() error) error {
|
| + lockItems, lockKeys := s.mkAllLockItems(keys)
|
| + if lockItems == nil {
|
| + return f()
|
| + }
|
| + if err := s.mc.SetMulti(lockItems); err != nil {
|
| + // this is a hard failure. No mutation can occur if we're unable to set
|
| + // locks out. See "DANGER ZONE" in the docs.
|
| + (log.Fields{log.ErrorKey: err}).Errorf(
|
| + s.c, "dscache: HARD FAILURE: supportContext.mutation(): mc.SetMulti")
|
| + return err
|
| + }
|
| + err := f()
|
| + if err == nil {
|
| + if err := s.mc.DeleteMulti(lockKeys); err != nil {
|
| + (log.Fields{log.ErrorKey: err}).Warningf(
|
| + s.c, "dscache: mc.DeleteMulti")
|
| + }
|
| + }
|
| + return err
|
| +}
|
| +
|
| +func (s *supportContext) mkRandLockItems(keys []ds.Key, metas ds.MultiMetaGetter) ([]memcache.Item, []byte) {
|
| + mcKeys := s.mkRandKeys(keys, metas)
|
| + if len(mcKeys) == 0 {
|
| + return nil, nil
|
| + }
|
| + nonce := s.crappyNonce()
|
| + ret := make([]memcache.Item, len(mcKeys))
|
| + for i, k := range mcKeys {
|
| + if k == "" {
|
| + continue
|
| + }
|
| + ret[i] = (s.mc.NewItem(k).
|
| + SetFlags(uint32(ItemHasLock)).
|
| + SetExpiration(time.Second * time.Duration(LockTimeSeconds)).
|
| + SetValue(nonce))
|
| + }
|
| + return ret, nonce
|
| +}
|
| +
|
| +func (s *supportContext) mkAllLockItems(keys []ds.Key) ([]memcache.Item, []string) {
|
| + mcKeys := s.mkAllKeys(keys)
|
| + if mcKeys == nil {
|
| + return nil, nil
|
| + }
|
| + ret := make([]memcache.Item, len(mcKeys))
|
| + for i := range ret {
|
| + ret[i] = (s.mc.NewItem(mcKeys[i]).
|
| + SetFlags(uint32(ItemHasLock)).
|
| + SetExpiration(time.Second * time.Duration(LockTimeSeconds)))
|
| + }
|
| + return ret, mcKeys
|
| +}
|
|
|