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 }