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

Side by Side Diff: filter/dscache/support.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/serialize.go ('k') | impl/memory/memcache.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 "fmt"
9 "math/rand"
10 "time"
11
12 ds "github.com/luci/gae/service/datastore"
13 "github.com/luci/gae/service/memcache"
14 log "github.com/luci/luci-go/common/logging"
15 "golang.org/x/net/context"
16 )
17
18 type supportContext struct {
19 aid string
20 ns string
21
22 c context.Context
23 mc memcache.Interface
24 mr *rand.Rand
25 shardsForKey func(ds.Key) int
26 }
27
28 func (s *supportContext) numShards(k ds.Key) int {
29 ret := DefaultShards
30 if s.shardsForKey != nil {
31 ret = s.shardsForKey(k)
32 }
33 if ret < 1 {
34 return 0 // disable caching entirely
35 }
36 if ret > MaxShards {
37 ret = MaxShards
38 }
39 return ret
40 }
41
42 func (s *supportContext) mkRandKeys(keys []ds.Key, metas ds.MultiMetaGetter) []s tring {
43 ret := []string(nil)
44 for i, key := range keys {
45 if !metas.GetMetaDefault(i, CacheEnableMeta, true).(bool) {
46 continue
47 }
48 shards := s.numShards(key)
49 if shards == 0 {
50 continue
51 }
52 if ret == nil {
53 ret = make([]string, len(keys))
54 }
55 ret[i] = MakeMemcacheKey(s.mr.Intn(shards), key)
56 }
57 return ret
58 }
59
60 func (s *supportContext) mkAllKeys(keys []ds.Key) []string {
61 size := 0
62 nums := make([]int, len(keys))
63 for i, k := range keys {
64 if !ds.KeyIncomplete(k) {
65 shards := s.numShards(k)
66 nums[i] = shards
67 size += shards
68 }
69 }
70 if size == 0 {
71 return nil
72 }
73 ret := make([]string, 0, size)
74 for i, key := range keys {
75 if !ds.KeyIncomplete(key) {
76 keySuffix := HashKey(key)
77 for shard := 0; shard < nums[i]; shard++ {
78 ret = append(ret, fmt.Sprintf(KeyFormat, shard, keySuffix))
79 }
80 }
81 }
82 return ret
83 }
84
85 // crappyNonce creates a really crappy nonce using math/rand. This is generally
86 // unacceptable for cryptographic purposes, but since mathrand is the only
87 // mocked randomness source, we use that.
88 //
89 // The random values here are controlled entriely by the application, will never
90 // be shown to, or provided by, the user, so this should be fine.
91 //
92 // Do not use this function for anything other than mkRandLockItems or your hair
93 // will fall out. You've been warned.
94 func (s *supportContext) crappyNonce() []byte {
95 ret := make([]byte, NonceUint32s*4)
96 for w := uint(0); w < NonceUint32s; w++ {
97 word := s.mr.Uint32()
98 for i := uint(0); i < 4; i++ {
99 ret[(w*4)+i] = byte(word >> (8 * i))
100 }
101 }
102 return ret
103 }
104
105 func (s *supportContext) mutation(keys []ds.Key, f func() error) error {
106 lockItems, lockKeys := s.mkAllLockItems(keys)
107 if lockItems == nil {
108 return f()
109 }
110 if err := s.mc.SetMulti(lockItems); err != nil {
111 // this is a hard failure. No mutation can occur if we're unable to set
112 // locks out. See "DANGER ZONE" in the docs.
113 (log.Fields{log.ErrorKey: err}).Errorf(
114 s.c, "dscache: HARD FAILURE: supportContext.mutation(): mc.SetMulti")
115 return err
116 }
117 err := f()
118 if err == nil {
119 if err := s.mc.DeleteMulti(lockKeys); err != nil {
120 (log.Fields{log.ErrorKey: err}).Warningf(
121 s.c, "dscache: mc.DeleteMulti")
122 }
123 }
124 return err
125 }
126
127 func (s *supportContext) mkRandLockItems(keys []ds.Key, metas ds.MultiMetaGetter ) ([]memcache.Item, []byte) {
128 mcKeys := s.mkRandKeys(keys, metas)
129 if len(mcKeys) == 0 {
130 return nil, nil
131 }
132 nonce := s.crappyNonce()
133 ret := make([]memcache.Item, len(mcKeys))
134 for i, k := range mcKeys {
135 if k == "" {
136 continue
137 }
138 ret[i] = (s.mc.NewItem(k).
139 SetFlags(uint32(ItemHasLock)).
140 SetExpiration(time.Second * time.Duration(LockTimeSecond s)).
141 SetValue(nonce))
142 }
143 return ret, nonce
144 }
145
146 func (s *supportContext) mkAllLockItems(keys []ds.Key) ([]memcache.Item, []strin g) {
147 mcKeys := s.mkAllKeys(keys)
148 if mcKeys == nil {
149 return nil, nil
150 }
151 ret := make([]memcache.Item, len(mcKeys))
152 for i := range ret {
153 ret[i] = (s.mc.NewItem(mcKeys[i]).
154 SetFlags(uint32(ItemHasLock)).
155 SetExpiration(time.Second * time.Duration(LockTimeSecond s)))
156 }
157 return ret, mcKeys
158 }
OLDNEW
« no previous file with comments | « filter/dscache/serialize.go ('k') | impl/memory/memcache.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698