github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/utils/cache/cache.go (about) 1 package cache 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 "go.uber.org/zap" 10 ) 11 12 // ExpirationNotifier is a function which will be called every time a cache 13 // expires an item 14 type ExpirationNotifier func(id interface{}, item interface{}) 15 16 // DataStore is the interface to a datastore. 17 type DataStore interface { 18 Add(u interface{}, value interface{}) (err error) 19 AddOrUpdate(u interface{}, value interface{}) bool 20 Get(u interface{}) (i interface{}, err error) 21 GetReset(u interface{}, duration time.Duration) (interface{}, error) 22 Remove(u interface{}) (err error) 23 RemoveWithDelay(u interface{}, duration time.Duration) (err error) 24 LockedModify(u interface{}, add func(a, b interface{}) interface{}, increment interface{}) (interface{}, error) 25 SetTimeOut(u interface{}, timeout time.Duration) (err error) 26 KeyList() []interface{} 27 ToString() string 28 } 29 30 // Cache is the structure that involves the map of entries. The cache 31 // provides a sync mechanism and allows multiple clients at the same time. 32 type Cache struct { 33 name string 34 data map[interface{}]entry 35 lifetime time.Duration 36 sync.RWMutex 37 expirer ExpirationNotifier 38 max int 39 } 40 41 // entry is a single line in the datastore that includes the actual entry 42 // and the time that entry was created or updated 43 type entry struct { 44 value interface{} 45 timestamp time.Time 46 timer *time.Timer 47 expirer ExpirationNotifier 48 } 49 50 // cacheRegistry keeps handles of all caches initialized through this library 51 // for book keeping 52 type cacheRegistry struct { 53 sync.RWMutex 54 items map[string]*Cache 55 } 56 57 var registry *cacheRegistry 58 59 func init() { 60 61 registry = &cacheRegistry{ 62 items: make(map[string]*Cache), 63 } 64 } 65 66 // Add adds a cache to a registry 67 func (r *cacheRegistry) Add(c *Cache) { 68 r.Lock() 69 defer r.Unlock() 70 71 r.items[c.name] = c 72 } 73 74 // ToString generates information about all caches initialized through this lib 75 func (r *cacheRegistry) ToString() string { 76 r.Lock() 77 defer r.Unlock() 78 79 buffer := fmt.Sprintf("Cache Registry: %d\n", len(r.items)) 80 buffer += fmt.Sprintf(" %32s : %s\n\n", "Cache Name", "max/curr") 81 for k, c := range r.items { 82 buffer += fmt.Sprintf(" %32s : %s\n", k, c.ToString()) 83 } 84 return buffer 85 } 86 87 // NewCache creates a new data cache 88 func NewCache(name string) *Cache { 89 90 return NewCacheWithExpirationNotifier(name, -1, nil) 91 } 92 93 // NewCacheWithExpiration creates a new data cache 94 func NewCacheWithExpiration(name string, lifetime time.Duration) *Cache { 95 96 return NewCacheWithExpirationNotifier(name, lifetime, nil) 97 } 98 99 // NewCacheWithExpirationNotifier creates a new data cache with notifier 100 func NewCacheWithExpirationNotifier(name string, lifetime time.Duration, expirer ExpirationNotifier) *Cache { 101 102 c := &Cache{ 103 name: name, 104 data: make(map[interface{}]entry), 105 lifetime: lifetime, 106 expirer: expirer, 107 } 108 c.max = len(c.data) 109 registry.Add(c) 110 return c 111 } 112 113 // ToString generates information about all caches initialized through this lib 114 func ToString() string { 115 116 return registry.ToString() 117 } 118 119 // ToString provides statistics about this cache 120 func (c *Cache) ToString() string { 121 c.Lock() 122 defer c.Unlock() 123 124 return fmt.Sprintf("%d/%d", c.max, len(c.data)) 125 } 126 127 // Add stores an entry into the cache and updates the timestamp 128 func (c *Cache) Add(u interface{}, value interface{}) (err error) { 129 130 var timer *time.Timer 131 if c.lifetime != -1 { 132 timer = time.AfterFunc(c.lifetime, func() { 133 if err := c.removeNotify(u, true); err != nil { 134 zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u))) 135 } 136 }) 137 } 138 139 t := time.Now() 140 141 c.Lock() 142 defer c.Unlock() 143 144 if _, ok := c.data[u]; !ok { 145 146 c.data[u] = entry{ 147 value: value, 148 timestamp: t, 149 timer: timer, 150 expirer: c.expirer, 151 } 152 if len(c.data) > c.max { 153 c.max = len(c.data) 154 } 155 return nil 156 } 157 158 return errors.New("item exists: use update") 159 } 160 161 // GetReset changes the value of an entry into the cache and updates the timestamp 162 func (c *Cache) GetReset(u interface{}, duration time.Duration) (interface{}, error) { 163 164 c.Lock() 165 defer c.Unlock() 166 167 if line, ok := c.data[u]; ok { 168 169 if c.lifetime != -1 && line.timer != nil { 170 if duration > 0 { 171 line.timer.Reset(duration) 172 } else { 173 line.timer.Reset(c.lifetime) 174 } 175 } 176 177 return line.value, nil 178 } 179 180 return nil, errors.New("cannot read item: not found") 181 } 182 183 // Update changes the value of an entry into the cache and updates the timestamp 184 func (c *Cache) Update(u interface{}, value interface{}) (err error) { 185 186 var timer *time.Timer 187 if c.lifetime != -1 { 188 timer = time.AfterFunc(c.lifetime, func() { 189 if err := c.removeNotify(u, true); err != nil { 190 zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u))) 191 } 192 }) 193 } 194 195 t := time.Now() 196 197 c.Lock() 198 defer c.Unlock() 199 200 if _, ok := c.data[u]; ok { 201 202 if c.data[u].timer != nil { 203 c.data[u].timer.Stop() 204 } 205 206 c.data[u] = entry{ 207 value: value, 208 timestamp: t, 209 timer: timer, 210 expirer: c.expirer, 211 } 212 213 return nil 214 } 215 216 return errors.New("cannot update item: not found") 217 } 218 219 // AddOrUpdate adds a new value in the cache or updates the existing value 220 // if needed. If an update happens the timestamp is also updated. 221 // Returns true if key was updated. 222 func (c *Cache) AddOrUpdate(u interface{}, value interface{}) (updated bool) { 223 224 var timer *time.Timer 225 if c.lifetime != -1 { 226 timer = time.AfterFunc(c.lifetime, func() { 227 if err := c.removeNotify(u, true); err != nil { 228 zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u))) 229 } 230 }) 231 } 232 233 t := time.Now() 234 235 c.Lock() 236 defer c.Unlock() 237 238 if _, updated = c.data[u]; updated { 239 if c.data[u].timer != nil { 240 c.data[u].timer.Stop() 241 } 242 } 243 244 c.data[u] = entry{ 245 value: value, 246 timestamp: t, 247 timer: timer, 248 expirer: c.expirer, 249 } 250 if len(c.data) > c.max { 251 c.max = len(c.data) 252 } 253 254 return updated 255 } 256 257 // SetTimeOut sets the time out of an entry to a new value 258 func (c *Cache) SetTimeOut(u interface{}, timeout time.Duration) (err error) { 259 c.Lock() 260 defer c.Unlock() 261 262 if _, ok := c.data[u]; !ok { 263 return errors.New("item is already deleted") 264 } 265 266 c.data[u].timer.Reset(timeout) 267 268 return nil 269 } 270 271 // Get retrieves the entry from the cache 272 func (c *Cache) Get(u interface{}) (i interface{}, err error) { 273 274 c.Lock() 275 defer c.Unlock() 276 277 if _, ok := c.data[u]; !ok { 278 return nil, errors.New("not found") 279 } 280 281 return c.data[u].value, nil 282 } 283 284 // KeyList returns all the keys that are currently stored in the cache. 285 func (c *Cache) KeyList() []interface{} { 286 c.Lock() 287 defer c.Unlock() 288 289 list := []interface{}{} 290 for k := range c.data { 291 list = append(list, k) 292 } 293 return list 294 } 295 296 // removeNotify removes the entry from the cache and optionally notifies. 297 // returns error if not there 298 func (c *Cache) removeNotify(u interface{}, notify bool) (err error) { 299 300 c.Lock() 301 302 val, ok := c.data[u] 303 if !ok { 304 c.Unlock() 305 return errors.New("not found") 306 } 307 308 if val.timer != nil { 309 val.timer.Stop() 310 } 311 delete(c.data, u) 312 c.Unlock() 313 314 if notify && val.expirer != nil { 315 val.expirer(u, val.value) 316 } 317 return nil 318 } 319 320 // Remove removes the entry from the cache and returns error if not there 321 func (c *Cache) Remove(u interface{}) (err error) { 322 323 return c.removeNotify(u, false) 324 } 325 326 // RemoveWithDelay removes the entry from the cache after a certain duration 327 func (c *Cache) RemoveWithDelay(u interface{}, duration time.Duration) error { 328 if duration == -1 { 329 return c.Remove(u) 330 } 331 332 c.Lock() 333 defer c.Unlock() 334 335 e, ok := c.data[u] 336 337 if !ok { 338 return errors.New("cannot remove item with delay: not found") 339 } 340 341 timer := time.AfterFunc(duration, func() { 342 if err := c.Remove(u); err != nil { 343 zap.L().Warn("Failed to remove item with delay", zap.String("key", fmt.Sprintf("%v", u)), zap.String("delay", duration.String())) 344 } 345 }) 346 347 t := time.Now() 348 349 if c.data[u].timer != nil { 350 c.data[u].timer.Stop() 351 } 352 353 c.data[u] = entry{ 354 value: e.value, 355 timestamp: t, 356 timer: timer, 357 expirer: c.expirer, 358 } 359 360 return nil 361 362 } 363 364 // SizeOf returns the number of elements in the cache 365 func (c *Cache) SizeOf() int { 366 367 c.Lock() 368 defer c.Unlock() 369 370 return len(c.data) 371 } 372 373 // LockedModify locks the data store 374 func (c *Cache) LockedModify(u interface{}, add func(a, b interface{}) interface{}, increment interface{}) (interface{}, error) { 375 376 var timer *time.Timer 377 if c.lifetime != -1 { 378 timer = time.AfterFunc(c.lifetime, func() { 379 if err := c.removeNotify(u, true); err != nil { 380 zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u))) 381 } 382 }) 383 } 384 385 t := time.Now() 386 387 c.Lock() 388 defer c.Unlock() 389 390 e, ok := c.data[u] 391 if !ok { 392 return nil, errors.New("not found") 393 } 394 395 if e.timer != nil { 396 e.timer.Stop() 397 } 398 399 e.value = add(e.value, increment) 400 e.timer = timer 401 e.timestamp = t 402 e.expirer = c.expirer 403 404 c.data[u] = e 405 406 return e.value, nil 407 408 }