github.com/jxskiss/gopkg@v0.17.3/lru/sharded.go (about)

     1  package lru
     2  
     3  import (
     4  	"github.com/jxskiss/gopkg/internal"
     5  	"github.com/jxskiss/gopkg/rthash"
     6  	"reflect"
     7  	"time"
     8  )
     9  
    10  var shardingHash = rthash.New()
    11  
    12  // NewShardedCache returns a hash-sharded lru cache instance which is suitable
    13  // to use for heavy lock contention use-case. It keeps same interface with
    14  // the lru cache instance returned by NewCache function.
    15  // Generally NewCache should be used instead of this unless you are sure that
    16  // you are facing the lock contention problem.
    17  func NewShardedCache(buckets, bucketCapacity int) *ShardedCache {
    18  	buckets = internal.NextPowerOfTwo(buckets)
    19  	mask := uintptr(buckets - 1)
    20  	mc := &ShardedCache{
    21  		buckets: uintptr(buckets),
    22  		mask:    mask,
    23  		cache:   make([]*Cache, buckets),
    24  	}
    25  	for i := 0; i < buckets; i++ {
    26  		mc.cache[i] = NewCache(bucketCapacity)
    27  	}
    28  	return mc
    29  }
    30  
    31  // ShardedCache is a hash-sharded version of Cache, it minimizes lock
    32  // contention for heavy read workload. Generally Cache should be used
    33  // instead of this unless you are sure that you are facing the lock
    34  // contention problem.
    35  //
    36  // It implements Interface in this package, see Interface for detailed
    37  // api documents.
    38  type ShardedCache struct {
    39  	buckets uintptr
    40  	mask    uintptr
    41  	cache   []*Cache
    42  }
    43  
    44  func (c *ShardedCache) Len() (n int) {
    45  	for _, c := range c.cache {
    46  		n += c.Len()
    47  	}
    48  	return
    49  }
    50  
    51  func (c *ShardedCache) Has(key interface{}) (exists, expired bool) {
    52  	h := shardingHash.Hash(key)
    53  	return c.cache[h&c.mask].Has(key)
    54  }
    55  
    56  func (c *ShardedCache) Get(key interface{}) (v interface{}, exists, expired bool) {
    57  	h := shardingHash.Hash(key)
    58  	return c.cache[h&c.mask].Get(key)
    59  }
    60  
    61  func (c *ShardedCache) GetWithTTL(key interface{}) (v interface{}, exists bool, ttl *time.Duration) {
    62  	h := shardingHash.Hash(key)
    63  	return c.cache[h&c.mask].GetWithTTL(key)
    64  }
    65  
    66  func (c *ShardedCache) GetQuiet(key interface{}) (v interface{}, exists, expired bool) {
    67  	h := shardingHash.Hash(key)
    68  	return c.cache[h&c.mask].GetQuiet(key)
    69  }
    70  
    71  func (c *ShardedCache) GetNotStale(key interface{}) (v interface{}, exists bool) {
    72  	h := shardingHash.Hash(key)
    73  	return c.cache[h&c.mask].GetNotStale(key)
    74  }
    75  
    76  func (c *ShardedCache) MGet(keys ...interface{}) map[interface{}]interface{} {
    77  	return c.mget(false, keys...)
    78  }
    79  
    80  func (c *ShardedCache) MGetNotStale(keys ...interface{}) map[interface{}]interface{} {
    81  	return c.mget(true, keys...)
    82  }
    83  
    84  func (c *ShardedCache) mget(notStale bool, keys ...interface{}) map[interface{}]interface{} {
    85  	grpKeys := c.groupKeys(keys)
    86  	nowNano := time.Now().UnixNano()
    87  
    88  	var res map[interface{}]interface{}
    89  	for idx, keys := range grpKeys {
    90  		grp := c.cache[idx].mget(notStale, nowNano, keys...)
    91  		if res == nil {
    92  			res = grp
    93  		} else {
    94  			for k, v := range grp {
    95  				res[k] = v
    96  			}
    97  		}
    98  	}
    99  	return res
   100  }
   101  
   102  func (c *ShardedCache) MGetInt(keys ...int) map[int]interface{} {
   103  	return c.mgetInt(false, keys...)
   104  }
   105  
   106  func (c *ShardedCache) MGetIntNotStale(keys ...int) map[int]interface{} {
   107  	return c.mgetInt(true, keys...)
   108  }
   109  
   110  func (c *ShardedCache) mgetInt(notStale bool, keys ...int) map[int]interface{} {
   111  	grpKeys := c.groupIntKeys(keys)
   112  	nowNano := time.Now().UnixNano()
   113  
   114  	var res map[int]interface{}
   115  	for idx, keys := range grpKeys {
   116  		grp := c.cache[idx].mgetInt(notStale, nowNano, keys...)
   117  		if res == nil {
   118  			res = grp
   119  		} else {
   120  			for k, v := range grp {
   121  				res[k] = v
   122  			}
   123  		}
   124  	}
   125  	return res
   126  }
   127  
   128  func (c *ShardedCache) MGetInt64(keys ...int64) map[int64]interface{} {
   129  	return c.mgetInt64(false, keys...)
   130  }
   131  
   132  func (c *ShardedCache) MGetInt64NotStale(keys ...int64) map[int64]interface{} {
   133  	return c.mgetInt64(true, keys...)
   134  }
   135  
   136  func (c *ShardedCache) mgetInt64(notStale bool, keys ...int64) map[int64]interface{} {
   137  	grpKeys := c.groupInt64Keys(keys)
   138  	nowNano := time.Now().UnixNano()
   139  
   140  	var res map[int64]interface{}
   141  	for idx, keys := range grpKeys {
   142  		grp := c.cache[idx].mgetInt64(notStale, nowNano, keys...)
   143  		if res == nil {
   144  			res = grp
   145  		} else {
   146  			for k, v := range grp {
   147  				res[k] = v
   148  			}
   149  		}
   150  	}
   151  	return res
   152  }
   153  
   154  func (c *ShardedCache) MGetUint64(keys ...uint64) map[uint64]interface{} {
   155  	return c.mgetUint64(false, keys...)
   156  }
   157  
   158  func (c *ShardedCache) MGetUint64NotStale(keys ...uint64) map[uint64]interface{} {
   159  	return c.mgetUint64(true, keys...)
   160  }
   161  
   162  func (c *ShardedCache) mgetUint64(notStale bool, keys ...uint64) map[uint64]interface{} {
   163  	grpKeys := c.groupUint64Keys(keys)
   164  	nowNano := time.Now().UnixNano()
   165  
   166  	var res map[uint64]interface{}
   167  	for idx, keys := range grpKeys {
   168  		grp := c.cache[idx].mgetUint64(notStale, nowNano, keys...)
   169  		if res == nil {
   170  			res = grp
   171  		} else {
   172  			for k, v := range grp {
   173  				res[k] = v
   174  			}
   175  		}
   176  	}
   177  	return res
   178  }
   179  
   180  func (c *ShardedCache) MGetString(keys ...string) map[string]interface{} {
   181  	return c.mgetString(false, keys...)
   182  }
   183  
   184  func (c *ShardedCache) MGetStringNotStale(keys ...string) map[string]interface{} {
   185  	return c.mgetString(true, keys...)
   186  }
   187  
   188  func (c *ShardedCache) mgetString(notStale bool, keys ...string) map[string]interface{} {
   189  	grpKeys := c.groupStringKeys(keys)
   190  	nowNano := time.Now().UnixNano()
   191  
   192  	var res map[string]interface{}
   193  	for idx, keys := range grpKeys {
   194  		grp := c.cache[idx].mgetString(notStale, nowNano, keys...)
   195  		if res == nil {
   196  			res = grp
   197  		} else {
   198  			for k, v := range grp {
   199  				res[k] = v
   200  			}
   201  		}
   202  	}
   203  	return res
   204  }
   205  
   206  func (c *ShardedCache) Set(key, value interface{}, ttl time.Duration) {
   207  	h := shardingHash.Hash(key)
   208  	c.cache[h&c.mask].Set(key, value, ttl)
   209  }
   210  
   211  func (c *ShardedCache) MSet(kvmap interface{}, ttl time.Duration) {
   212  	m := reflect.ValueOf(kvmap)
   213  	keys := m.MapKeys()
   214  
   215  	for _, key := range keys {
   216  		value := m.MapIndex(key)
   217  		c.Set(key.Interface(), value.Interface(), ttl)
   218  	}
   219  }
   220  
   221  func (c *ShardedCache) Del(key interface{}) {
   222  	h := shardingHash.Hash(key)
   223  	c.cache[h&c.mask].Del(key)
   224  }
   225  
   226  func (c *ShardedCache) MDel(keys ...interface{}) {
   227  	grpKeys := c.groupKeys(keys)
   228  
   229  	for idx, keys := range grpKeys {
   230  		c.cache[idx].MDel(keys...)
   231  	}
   232  }
   233  
   234  func (c *ShardedCache) MDelInt(keys ...int) {
   235  	grpKeys := c.groupIntKeys(keys)
   236  
   237  	for idx, keys := range grpKeys {
   238  		c.cache[idx].MDelInt(keys...)
   239  	}
   240  }
   241  
   242  func (c *ShardedCache) MDelInt64(keys ...int64) {
   243  	grpKeys := c.groupInt64Keys(keys)
   244  
   245  	for idx, keys := range grpKeys {
   246  		c.cache[idx].MDelInt64(keys...)
   247  	}
   248  }
   249  
   250  func (c *ShardedCache) MDelUint64(keys ...uint64) {
   251  	grpKeys := c.groupUint64Keys(keys)
   252  
   253  	for idx, keys := range grpKeys {
   254  		c.cache[idx].MDelUint64(keys...)
   255  	}
   256  }
   257  
   258  func (c *ShardedCache) MDelString(keys ...string) {
   259  	grpKeys := c.groupStringKeys(keys)
   260  
   261  	for idx, keys := range grpKeys {
   262  		c.cache[idx].MDelString(keys...)
   263  	}
   264  }
   265  
   266  func (c *ShardedCache) groupKeys(keys []interface{}) map[uintptr][]interface{} {
   267  	grpKeys := make(map[uintptr][]interface{})
   268  	for _, key := range keys {
   269  		idx := shardingHash.Interface(key) & c.mask
   270  		grpKeys[idx] = append(grpKeys[idx], key)
   271  	}
   272  	return grpKeys
   273  }
   274  
   275  func (c *ShardedCache) groupIntKeys(keys []int) map[uintptr][]int {
   276  	grpKeys := make(map[uintptr][]int)
   277  	for _, key := range keys {
   278  		idx := shardingHash.Int(key) & c.mask
   279  		grpKeys[idx] = append(grpKeys[idx], key)
   280  	}
   281  	return grpKeys
   282  }
   283  
   284  func (c *ShardedCache) groupInt64Keys(keys []int64) map[uintptr][]int64 {
   285  	grpKeys := make(map[uintptr][]int64)
   286  	for _, key := range keys {
   287  		idx := shardingHash.Int64(key) & c.mask
   288  		grpKeys[idx] = append(grpKeys[idx], key)
   289  	}
   290  	return grpKeys
   291  }
   292  
   293  func (c *ShardedCache) groupUint64Keys(keys []uint64) map[uintptr][]uint64 {
   294  	grpKeys := make(map[uintptr][]uint64)
   295  	for _, key := range keys {
   296  		idx := shardingHash.Uint64(key) & c.mask
   297  		grpKeys[idx] = append(grpKeys[idx], key)
   298  	}
   299  	return grpKeys
   300  }
   301  
   302  func (c *ShardedCache) groupStringKeys(keys []string) map[uintptr][]string {
   303  	grpKeys := make(map[uintptr][]string)
   304  	for _, key := range keys {
   305  		idx := shardingHash.String(key) & c.mask
   306  		grpKeys[idx] = append(grpKeys[idx], key)
   307  	}
   308  	return grpKeys
   309  }