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  }