github.com/qiniu/x@v1.11.9/objcache/objcache.go (about) 1 package objcache 2 3 import ( 4 "sync" 5 6 "github.com/qiniu/x/objcache/lru" 7 ) 8 9 // Key type. 10 type Key = lru.Key 11 12 // Value type. 13 type Value = interface{} 14 15 // Context type. 16 type Context = interface{} 17 18 // OnEvictedFunc func. 19 type OnEvictedFunc = func(key Key, value Value) 20 21 // A GetterFunc implements Getter with a function. 22 type GetterFunc = func(ctx Context, key Key) (val Value, err error) 23 24 // newGroupHook, if non-nil, is called right after a new group is created. 25 var newGroupHook func(*Group) 26 27 // RegisterNewGroupHook registers a hook that is run each time 28 // a group is created. 29 func RegisterNewGroupHook(fn func(*Group)) { 30 if newGroupHook != nil { 31 panic("RegisterNewGroupHook called more than once") 32 } 33 newGroupHook = fn 34 } 35 36 // A Group is a cache namespace and associated data loaded spread over 37 // a group of 1 or more machines. 38 type Group struct { 39 name string 40 get GetterFunc 41 42 mainCache cache 43 } 44 45 var ( 46 mu sync.RWMutex 47 groups = make(map[string]*Group) 48 ) 49 50 // GetGroup returns the named group previously created with NewGroup, or 51 // nil if there's no such group. 52 func GetGroup(name string) *Group { 53 mu.RLock() 54 g := groups[name] 55 mu.RUnlock() 56 return g 57 } 58 59 // NewGroup creates a coordinated group-aware Getter from a Getter. 60 // 61 // The returned Getter tries (but does not guarantee) to run only one 62 // Get call at once for a given key across an entire set of peer 63 // processes. Concurrent callers both in the local process and in 64 // other processes receive copies of the answer once the original Get 65 // completes. 66 // 67 // The group name must be unique for each getter. 68 func NewGroup(name string, cacheNum int, getter GetterFunc, onEvicted ...OnEvictedFunc) *Group { 69 mu.Lock() 70 defer mu.Unlock() 71 if _, dup := groups[name]; dup { 72 panic("duplicate registration of group " + name) 73 } 74 g := &Group{ 75 name: name, 76 get: getter, 77 } 78 g.mainCache.init(cacheNum, onEvicted...) 79 if newGroupHook != nil { 80 newGroupHook(g) 81 } 82 groups[name] = g 83 return g 84 } 85 86 // Name returns the name of the group. 87 func (g *Group) Name() string { 88 return g.name 89 } 90 91 // Get func. 92 func (g *Group) Get(ctx Context, key Key) (val Value, err error) { 93 val, ok := g.mainCache.get(key) 94 if ok { 95 return 96 } 97 val, err = g.get(ctx, key) 98 if err == nil { 99 g.mainCache.add(key, val) 100 } 101 return 102 } 103 104 // TryGet func. 105 func (g *Group) TryGet(key Key) (val Value, ok bool) { 106 return g.mainCache.get(key) 107 } 108 109 // CacheStats returns stats about the provided cache within the group. 110 func (g *Group) CacheStats() CacheStats { 111 return g.mainCache.stats() 112 } 113 114 // cache is a wrapper around an *lru.Cache that adds synchronization, 115 // makes values always be ByteView, and counts the size of all keys and 116 // values. 117 type cache struct { 118 mu sync.RWMutex 119 lru *lru.Cache 120 nhit, nget int64 121 } 122 123 func (c *cache) stats() CacheStats { 124 c.mu.RLock() 125 defer c.mu.RUnlock() 126 return CacheStats{ 127 Items: c.itemsLocked(), 128 Gets: c.nget, 129 Hits: c.nhit, 130 } 131 } 132 133 func (c *cache) init(cacheNum int, onEvicted ...OnEvictedFunc) { 134 c.lru = lru.New(cacheNum) 135 if onEvicted != nil { 136 c.lru.OnEvicted = onEvicted[0] 137 } 138 } 139 140 func (c *cache) add(key Key, value Value) { 141 c.mu.Lock() 142 defer c.mu.Unlock() 143 c.lru.Add(key, value) 144 } 145 146 func (c *cache) get(key Key) (value Value, ok bool) { 147 c.mu.Lock() 148 defer c.mu.Unlock() 149 c.nget++ 150 v, ok := c.lru.Get(key) 151 if ok { 152 value = v.(Value) 153 c.nhit++ 154 } 155 return 156 } 157 158 func (c *cache) items() int64 { 159 c.mu.RLock() 160 defer c.mu.RUnlock() 161 return c.itemsLocked() 162 } 163 164 func (c *cache) itemsLocked() int64 { 165 return int64(c.lru.Len()) 166 } 167 168 // CacheStats are returned by stats accessors on Group. 169 type CacheStats struct { 170 Items int64 171 Gets int64 172 Hits int64 173 }