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

Side by Side Diff: server/auth/cache.go

Issue 2646733008: server/auth: Add in-process LRU-based cache. (Closed)
Patch Set: Rename GlobalCache => Cache, ProcCache => MemoryCache. Created 3 years, 11 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 | « appengine/gaemiddleware/context.go ('k') | server/auth/cache_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The LUCI Authors. All rights reserved. 1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package auth 5 package auth
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "crypto/sha1" 9 "crypto/sha1"
10 "encoding/base64" 10 "encoding/base64"
11 "encoding/gob" 11 "encoding/gob"
12 "fmt" 12 "fmt"
13 "time" 13 "time"
14 14
15 "golang.org/x/net/context" 15 "golang.org/x/net/context"
16 16
17 "github.com/luci/luci-go/common/clock" 17 "github.com/luci/luci-go/common/clock"
18 "github.com/luci/luci-go/common/data/caching/lru"
18 "github.com/luci/luci-go/common/data/rand/mathrand" 19 "github.com/luci/luci-go/common/data/rand/mathrand"
19 ) 20 )
20 21
21 // delegationTokenCache is used to store delegation tokens in the global cache. 22 // delegationTokenCache is used to store delegation tokens in the cache.
22 var delegationTokenCache = tokenCache{ 23 var delegationTokenCache = tokenCache{
23 Kind: "delegation", 24 Kind: "delegation",
24 Version: 2, 25 Version: 2,
25 ExpRandPercent: 10, 26 ExpRandPercent: 10,
26 MinAcceptedLifetime: 5 * time.Minute, 27 MinAcceptedLifetime: 5 * time.Minute,
27 } 28 }
28 29
29 // tokenCache knows how to keep tokens in the global cache. 30 // tokenCache knows how to keep tokens in the cache.
30 // 31 //
31 // Uses Config.GlobalCache as a storage backend. 32 // Uses Config.Cache as a storage backend.
32 // 33 //
33 // It implements probabilistic early expiration to workaround cache stampede 34 // It implements probabilistic early expiration to workaround cache stampede
34 // problem for hot items, see Fetch. 35 // problem for hot items, see Fetch.
35 type tokenCache struct { 36 type tokenCache struct {
36 // Kind defines the token kind. Will be used as part of the cache key. 37 // Kind defines the token kind. Will be used as part of the cache key.
37 Kind string 38 Kind string
38 39
39 // Version defines format of the data. Will be used as part of the cache key. 40 // Version defines format of the data. Will be used as part of the cache key.
40 // 41 //
41 // If you change a type behind interface{} in Token field, you MUST bump the 42 // If you change a type behind interface{} in Token field, you MUST bump the
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 } 74 }
74 ttl := tok.Expiry.Sub(tok.Created) 75 ttl := tok.Expiry.Sub(tok.Created)
75 if ttl < tc.MinAcceptedLifetime { 76 if ttl < tc.MinAcceptedLifetime {
76 return fmt.Errorf("refusing to store a token that expires in %s" , ttl) 77 return fmt.Errorf("refusing to store a token that expires in %s" , ttl)
77 } 78 }
78 blob, err := tc.marshal(&tok) 79 blob, err := tc.marshal(&tok)
79 if err != nil { 80 if err != nil {
80 return err 81 return err
81 } 82 }
82 cfg := GetConfig(c) 83 cfg := GetConfig(c)
83 » if cfg == nil || cfg.GlobalCache == nil { 84 » if cfg == nil || cfg.Cache == nil {
84 return ErrNotConfigured 85 return ErrNotConfigured
85 } 86 }
86 » return cfg.GlobalCache.Set(c, tc.itemKey(tok.Key), blob, ttl) 87 » return cfg.Cache.Set(c, tc.itemKey(tok.Key), blob, ttl)
87 } 88 }
88 89
89 // Fetch grabs cached token if it hasn't expired yet. 90 // Fetch grabs cached token if it hasn't expired yet.
90 // 91 //
91 // Returns (nil, nil) if no such token or it has expired already. If the cached 92 // Returns (nil, nil) if no such token or it has expired already. If the cached
92 // token is close to expiration, this function will randomly return a cache 93 // token is close to expiration, this function will randomly return a cache
93 // miss. That way if multiple concurrent processes all constantly use the same 94 // miss. That way if multiple concurrent processes all constantly use the same
94 // token, only the most unlucky one will refresh it. 95 // token, only the most unlucky one will refresh it.
95 func (tc *tokenCache) Fetch(c context.Context, key string) (*cachedToken, error) { 96 func (tc *tokenCache) Fetch(c context.Context, key string) (*cachedToken, error) {
96 cfg := GetConfig(c) 97 cfg := GetConfig(c)
97 » if cfg == nil || cfg.GlobalCache == nil { 98 » if cfg == nil || cfg.Cache == nil {
98 return nil, ErrNotConfigured 99 return nil, ErrNotConfigured
99 } 100 }
100 101
101 » blob, err := cfg.GlobalCache.Get(c, tc.itemKey(key)) 102 » blob, err := cfg.Cache.Get(c, tc.itemKey(key))
102 switch { 103 switch {
103 case err != nil: 104 case err != nil:
104 return nil, err 105 return nil, err
105 case blob == nil: 106 case blob == nil:
106 return nil, nil 107 return nil, nil
107 } 108 }
108 109
109 tok, err := tc.unmarshal(blob) 110 tok, err := tc.unmarshal(blob)
110 if err != nil { 111 if err != nil {
111 return nil, err 112 return nil, err
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 154
154 // unmarshal is reverse of marshal. 155 // unmarshal is reverse of marshal.
155 func (tc *tokenCache) unmarshal(blob []byte) (*cachedToken, error) { 156 func (tc *tokenCache) unmarshal(blob []byte) (*cachedToken, error) {
156 dec := gob.NewDecoder(bytes.NewReader(blob)) 157 dec := gob.NewDecoder(bytes.NewReader(blob))
157 out := &cachedToken{} 158 out := &cachedToken{}
158 if err := dec.Decode(out); err != nil { 159 if err := dec.Decode(out); err != nil {
159 return nil, err 160 return nil, err
160 } 161 }
161 return out, nil 162 return out, nil
162 } 163 }
164
165 type memoryCache struct {
166 cache *lru.Cache
167 }
168
169 // MemoryCache creates a new in-memory Cache instance that is built on
170 // top of an LRU cache of the specified size.
171 func MemoryCache(size int) Cache {
172 return memoryCache{
173 cache: lru.New(size),
174 }
175 }
176
177 func (mc memoryCache) Get(c context.Context, key string) ([]byte, error) {
178 var item *memoryCacheItem
179 now := clock.Now(c)
180 _ = mc.cache.Mutate(key, func(cur interface{}) interface{} {
181 if cur == nil {
182 return nil
183 }
184
185 item = cur.(*memoryCacheItem)
186 if now.After(item.exp) {
187 // Cache item is too old, so expire it.
188 item = nil
189 }
190 return item
191 })
192 if item == nil {
193 // Cache miss (or expired).
194 return nil, nil
195 }
196 return item.value, nil
197 }
198
199 func (mc memoryCache) Set(c context.Context, key string, value []byte, exp time. Duration) error {
200 mc.cache.Put(key, &memoryCacheItem{
201 value: value,
202 exp: clock.Now(c).Add(exp),
203 })
204 return nil
205 }
206
207 type memoryCacheItem struct {
208 value []byte
209 exp time.Time
210 }
OLDNEW
« no previous file with comments | « appengine/gaemiddleware/context.go ('k') | server/auth/cache_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698