github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/groupcache/groupcache.go (about) 1 /* 2 Copyright 2012 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package groupcache provides a data loading mechanism with caching 18 // and de-duplication that works across a set of peer processes. 19 // 20 // Each data Get first consults its local cache, otherwise delegates 21 // to the requested key's canonical owner, which then checks its cache 22 // or finally gets the data. In the common case, many concurrent 23 // cache misses across a set of peers for the same key result in just 24 // one cache fill. 25 package groupcache 26 27 import ( 28 "errors" 29 "math/rand" 30 "strconv" 31 "sync" 32 "sync/atomic" 33 34 pb "github.com/insionng/yougam/libraries/golang/groupcache/groupcachepb" 35 "github.com/insionng/yougam/libraries/golang/groupcache/lru" 36 "github.com/insionng/yougam/libraries/golang/groupcache/singleflight" 37 ) 38 39 // A Getter loads data for a key. 40 type Getter interface { 41 // Get returns the value identified by key, populating dest. 42 // 43 // The returned data must be unversioned. That is, key must 44 // uniquely describe the loaded data, without an implicit 45 // current time, and without relying on cache expiration 46 // mechanisms. 47 Get(ctx Context, key string, dest Sink) error 48 } 49 50 // A GetterFunc implements Getter with a function. 51 type GetterFunc func(ctx Context, key string, dest Sink) error 52 53 func (f GetterFunc) Get(ctx Context, key string, dest Sink) error { 54 return f(ctx, key, dest) 55 } 56 57 var ( 58 mu sync.RWMutex 59 groups = make(map[string]*Group) 60 61 initPeerServerOnce sync.Once 62 initPeerServer func() 63 ) 64 65 // GetGroup returns the named group previously created with NewGroup, or 66 // nil if there's no such group. 67 func GetGroup(name string) *Group { 68 mu.RLock() 69 g := groups[name] 70 mu.RUnlock() 71 return g 72 } 73 74 // NewGroup creates a coordinated group-aware Getter from a Getter. 75 // 76 // The returned Getter tries (but does not guarantee) to run only one 77 // Get call at once for a given key across an entire set of peer 78 // processes. Concurrent callers both in the local process and in 79 // other processes receive copies of the answer once the original Get 80 // completes. 81 // 82 // The group name must be unique for each getter. 83 func NewGroup(name string, cacheBytes int64, getter Getter) *Group { 84 return newGroup(name, cacheBytes, getter, nil) 85 } 86 87 // If peers is nil, the peerPicker is called via a sync.Once to initialize it. 88 func newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group { 89 if getter == nil { 90 panic("nil Getter") 91 } 92 mu.Lock() 93 defer mu.Unlock() 94 initPeerServerOnce.Do(callInitPeerServer) 95 if _, dup := groups[name]; dup { 96 panic("duplicate registration of group " + name) 97 } 98 g := &Group{ 99 name: name, 100 getter: getter, 101 peers: peers, 102 cacheBytes: cacheBytes, 103 loadGroup: &singleflight.Group{}, 104 } 105 if fn := newGroupHook; fn != nil { 106 fn(g) 107 } 108 groups[name] = g 109 return g 110 } 111 112 // newGroupHook, if non-nil, is called right after a new group is created. 113 var newGroupHook func(*Group) 114 115 // RegisterNewGroupHook registers a hook that is run each time 116 // a group is created. 117 func RegisterNewGroupHook(fn func(*Group)) { 118 if newGroupHook != nil { 119 panic("RegisterNewGroupHook called more than once") 120 } 121 newGroupHook = fn 122 } 123 124 // RegisterServerStart registers a hook that is run when the first 125 // group is created. 126 func RegisterServerStart(fn func()) { 127 if initPeerServer != nil { 128 panic("RegisterServerStart called more than once") 129 } 130 initPeerServer = fn 131 } 132 133 func callInitPeerServer() { 134 if initPeerServer != nil { 135 initPeerServer() 136 } 137 } 138 139 // A Group is a cache namespace and associated data loaded spread over 140 // a group of 1 or more machines. 141 type Group struct { 142 name string 143 getter Getter 144 peersOnce sync.Once 145 peers PeerPicker 146 cacheBytes int64 // limit for sum of mainCache and hotCache size 147 148 // mainCache is a cache of the keys for which this process 149 // (amongst its peers) is authorative. That is, this cache 150 // contains keys which consistent hash on to this process's 151 // peer number. 152 mainCache cache 153 154 // hotCache contains keys/values for which this peer is not 155 // authorative (otherwise they would be in mainCache), but 156 // are popular enough to warrant mirroring in this process to 157 // avoid going over the network to fetch from a peer. Having 158 // a hotCache avoids network hotspotting, where a peer's 159 // network card could become the bottleneck on a popular key. 160 // This cache is used sparingly to maximize the total number 161 // of key/value pairs that can be stored globally. 162 hotCache cache 163 164 // loadGroup ensures that each key is only fetched once 165 // (either locally or remotely), regardless of the number of 166 // concurrent callers. 167 loadGroup flightGroup 168 169 // Stats are statistics on the group. 170 Stats Stats 171 } 172 173 // flightGroup is defined as an interface which flightgroup.Group 174 // satisfies. We define this so that we may test with an alternate 175 // implementation. 176 type flightGroup interface { 177 // Done is called when Do is done. 178 Do(key string, fn func() (interface{}, error)) (interface{}, error) 179 } 180 181 // Stats are per-group statistics. 182 type Stats struct { 183 Gets AtomicInt // any Get request, including from peers 184 CacheHits AtomicInt // either cache was good 185 PeerLoads AtomicInt // either remote load or remote cache hit (not an error) 186 PeerErrors AtomicInt 187 Loads AtomicInt // (gets - cacheHits) 188 LoadsDeduped AtomicInt // after singleflight 189 LocalLoads AtomicInt // total good local loads 190 LocalLoadErrs AtomicInt // total bad local loads 191 ServerRequests AtomicInt // gets that came over the network from peers 192 } 193 194 // Name returns the name of the group. 195 func (g *Group) Name() string { 196 return g.name 197 } 198 199 func (g *Group) initPeers() { 200 if g.peers == nil { 201 g.peers = getPeers() 202 } 203 } 204 205 func (g *Group) Get(ctx Context, key string, dest Sink) error { 206 g.peersOnce.Do(g.initPeers) 207 g.Stats.Gets.Add(1) 208 if dest == nil { 209 return errors.New("groupcache: nil dest Sink") 210 } 211 value, cacheHit := g.lookupCache(key) 212 213 if cacheHit { 214 g.Stats.CacheHits.Add(1) 215 return setSinkView(dest, value) 216 } 217 218 // Optimization to avoid double unmarshalling or copying: keep 219 // track of whether the dest was already populated. One caller 220 // (if local) will set this; the losers will not. The common 221 // case will likely be one caller. 222 destPopulated := false 223 value, destPopulated, err := g.load(ctx, key, dest) 224 if err != nil { 225 return err 226 } 227 if destPopulated { 228 return nil 229 } 230 return setSinkView(dest, value) 231 } 232 233 // load loads key either by invoking the getter locally or by sending it to another machine. 234 func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) { 235 g.Stats.Loads.Add(1) 236 viewi, err := g.loadGroup.Do(key, func() (interface{}, error) { 237 // Check the cache again because singleflight can only dedup calls 238 // that overlap concurrently. It's possible for 2 concurrent 239 // requests to miss the cache, resulting in 2 load() calls. An 240 // unfortunate goroutine scheduling would result in this callback 241 // being run twice, serially. If we don't check the cache again, 242 // cache.nbytes would be incremented below even though there will 243 // be only one entry for this key. 244 // 245 // Consider the following serialized event ordering for two 246 // goroutines in which this callback gets called twice for hte 247 // same key: 248 // 1: Get("key") 249 // 2: Get("key") 250 // 1: lookupCache("key") 251 // 2: lookupCache("key") 252 // 1: load("key") 253 // 2: load("key") 254 // 1: loadGroup.Do("key", fn) 255 // 1: fn() 256 // 2: loadGroup.Do("key", fn) 257 // 2: fn() 258 if value, cacheHit := g.lookupCache(key); cacheHit { 259 g.Stats.CacheHits.Add(1) 260 return value, nil 261 } 262 g.Stats.LoadsDeduped.Add(1) 263 var value ByteView 264 var err error 265 if peer, ok := g.peers.PickPeer(key); ok { 266 value, err = g.getFromPeer(ctx, peer, key) 267 if err == nil { 268 g.Stats.PeerLoads.Add(1) 269 return value, nil 270 } 271 g.Stats.PeerErrors.Add(1) 272 // TODO(bradfitz): log the peer's error? keep 273 // log of the past few for /groupcachez? It's 274 // probably boring (normal task movement), so not 275 // worth logging I imagine. 276 } 277 value, err = g.getLocally(ctx, key, dest) 278 if err != nil { 279 g.Stats.LocalLoadErrs.Add(1) 280 return nil, err 281 } 282 g.Stats.LocalLoads.Add(1) 283 destPopulated = true // only one caller of load gets this return value 284 g.populateCache(key, value, &g.mainCache) 285 return value, nil 286 }) 287 if err == nil { 288 value = viewi.(ByteView) 289 } 290 return 291 } 292 293 func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) { 294 err := g.getter.Get(ctx, key, dest) 295 if err != nil { 296 return ByteView{}, err 297 } 298 return dest.view() 299 } 300 301 func (g *Group) getFromPeer(ctx Context, peer ProtoGetter, key string) (ByteView, error) { 302 req := &pb.GetRequest{ 303 Group: &g.name, 304 Key: &key, 305 } 306 res := &pb.GetResponse{} 307 err := peer.Get(ctx, req, res) 308 if err != nil { 309 return ByteView{}, err 310 } 311 value := ByteView{b: res.Value} 312 // TODO(bradfitz): use res.MinuteQps or something smart to 313 // conditionally populate hotCache. For now just do it some 314 // percentage of the time. 315 if rand.Intn(10) == 0 { 316 g.populateCache(key, value, &g.hotCache) 317 } 318 return value, nil 319 } 320 321 func (g *Group) lookupCache(key string) (value ByteView, ok bool) { 322 if g.cacheBytes <= 0 { 323 return 324 } 325 value, ok = g.mainCache.get(key) 326 if ok { 327 return 328 } 329 value, ok = g.hotCache.get(key) 330 return 331 } 332 333 func (g *Group) populateCache(key string, value ByteView, cache *cache) { 334 if g.cacheBytes <= 0 { 335 return 336 } 337 cache.add(key, value) 338 339 // Evict items from cache(s) if necessary. 340 for { 341 mainBytes := g.mainCache.bytes() 342 hotBytes := g.hotCache.bytes() 343 if mainBytes+hotBytes <= g.cacheBytes { 344 return 345 } 346 347 // TODO(bradfitz): this is good-enough-for-now logic. 348 // It should be something based on measurements and/or 349 // respecting the costs of different resources. 350 victim := &g.mainCache 351 if hotBytes > mainBytes/8 { 352 victim = &g.hotCache 353 } 354 victim.removeOldest() 355 } 356 } 357 358 // CacheType represents a type of cache. 359 type CacheType int 360 361 const ( 362 // The MainCache is the cache for items that this peer is the 363 // owner for. 364 MainCache CacheType = iota + 1 365 366 // The HotCache is the cache for items that seem popular 367 // enough to replicate to this node, even though it's not the 368 // owner. 369 HotCache 370 ) 371 372 // CacheStats returns stats about the provided cache within the group. 373 func (g *Group) CacheStats(which CacheType) CacheStats { 374 switch which { 375 case MainCache: 376 return g.mainCache.stats() 377 case HotCache: 378 return g.hotCache.stats() 379 default: 380 return CacheStats{} 381 } 382 } 383 384 // cache is a wrapper around an *lru.Cache that adds synchronization, 385 // makes values always be ByteView, and counts the size of all keys and 386 // values. 387 type cache struct { 388 mu sync.RWMutex 389 nbytes int64 // of all keys and values 390 lru *lru.Cache 391 nhit, nget int64 392 nevict int64 // number of evictions 393 } 394 395 func (c *cache) stats() CacheStats { 396 c.mu.RLock() 397 defer c.mu.RUnlock() 398 return CacheStats{ 399 Bytes: c.nbytes, 400 Items: c.itemsLocked(), 401 Gets: c.nget, 402 Hits: c.nhit, 403 Evictions: c.nevict, 404 } 405 } 406 407 func (c *cache) add(key string, value ByteView) { 408 c.mu.Lock() 409 defer c.mu.Unlock() 410 if c.lru == nil { 411 c.lru = &lru.Cache{ 412 OnEvicted: func(key lru.Key, value interface{}) { 413 val := value.(ByteView) 414 c.nbytes -= int64(len(key.(string))) + int64(val.Len()) 415 c.nevict++ 416 }, 417 } 418 } 419 c.lru.Add(key, value) 420 c.nbytes += int64(len(key)) + int64(value.Len()) 421 } 422 423 func (c *cache) get(key string) (value ByteView, ok bool) { 424 c.mu.Lock() 425 defer c.mu.Unlock() 426 c.nget++ 427 if c.lru == nil { 428 return 429 } 430 vi, ok := c.lru.Get(key) 431 if !ok { 432 return 433 } 434 c.nhit++ 435 return vi.(ByteView), true 436 } 437 438 func (c *cache) removeOldest() { 439 c.mu.Lock() 440 defer c.mu.Unlock() 441 if c.lru != nil { 442 c.lru.RemoveOldest() 443 } 444 } 445 446 func (c *cache) bytes() int64 { 447 c.mu.RLock() 448 defer c.mu.RUnlock() 449 return c.nbytes 450 } 451 452 func (c *cache) items() int64 { 453 c.mu.RLock() 454 defer c.mu.RUnlock() 455 return c.itemsLocked() 456 } 457 458 func (c *cache) itemsLocked() int64 { 459 if c.lru == nil { 460 return 0 461 } 462 return int64(c.lru.Len()) 463 } 464 465 // An AtomicInt is an int64 to be accessed atomically. 466 type AtomicInt int64 467 468 // Add atomically adds n to i. 469 func (i *AtomicInt) Add(n int64) { 470 atomic.AddInt64((*int64)(i), n) 471 } 472 473 // Get atomically gets the value of i. 474 func (i *AtomicInt) Get() int64 { 475 return atomic.LoadInt64((*int64)(i)) 476 } 477 478 func (i *AtomicInt) String() string { 479 return strconv.FormatInt(i.Get(), 10) 480 } 481 482 // CacheStats are returned by stats accessors on Group. 483 type CacheStats struct { 484 Bytes int64 485 Items int64 486 Gets int64 487 Hits int64 488 Evictions int64 489 }