112 lines
2.2 KiB
Go
112 lines
2.2 KiB
Go
|
package cache
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var zeroTTL = time.Duration(0)
|
||
|
|
||
|
// MemoryTTL holds the required variables to compose an in memory cache system
|
||
|
// which also provides expiring key mechanism
|
||
|
type MemoryTTL struct {
|
||
|
// Mutex is used for handling the concurrent
|
||
|
// read/write requests for cache
|
||
|
sync.Mutex
|
||
|
|
||
|
// cache holds the cache data
|
||
|
cache *MemoryNoTS
|
||
|
|
||
|
// setAts holds the time that related item's set at
|
||
|
setAts map[string]time.Time
|
||
|
|
||
|
// ttl is a duration for a cache key to expire
|
||
|
ttl time.Duration
|
||
|
|
||
|
// gcInterval is a duration for garbage collection
|
||
|
gcInterval time.Duration
|
||
|
}
|
||
|
|
||
|
// NewMemoryWithTTL creates an inmemory cache system
|
||
|
// Which everytime will return the true values about a cache hit
|
||
|
// and never will leak memory
|
||
|
// ttl is used for expiration of a key from cache
|
||
|
func NewMemoryWithTTL(ttl time.Duration) *MemoryTTL {
|
||
|
return &MemoryTTL{
|
||
|
cache: NewMemoryNoTS(),
|
||
|
setAts: map[string]time.Time{},
|
||
|
ttl: ttl,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// StartGC starts the garbage collection process in a go routine
|
||
|
func (r *MemoryTTL) StartGC(gcInterval time.Duration) {
|
||
|
r.gcInterval = gcInterval
|
||
|
go func() {
|
||
|
for _ = range time.Tick(gcInterval) {
|
||
|
for key := range r.cache.items {
|
||
|
if !r.isValid(key) {
|
||
|
r.Delete(key)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
// Get returns a value of a given key if it exists
|
||
|
// and valid for the time being
|
||
|
func (r *MemoryTTL) Get(key string) (interface{}, error) {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
|
||
|
if !r.isValid(key) {
|
||
|
r.delete(key)
|
||
|
return nil, ErrNotFound
|
||
|
}
|
||
|
|
||
|
value, err := r.cache.Get(key)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
// Set will persist a value to the cache or
|
||
|
// override existing one with the new one
|
||
|
func (r *MemoryTTL) Set(key string, value interface{}) error {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
|
||
|
r.cache.Set(key, value)
|
||
|
r.setAts[key] = time.Now()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Delete deletes a given key if exists
|
||
|
func (r *MemoryTTL) Delete(key string) error {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
|
||
|
r.delete(key)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (r *MemoryTTL) delete(key string) {
|
||
|
r.cache.Delete(key)
|
||
|
delete(r.setAts, key)
|
||
|
}
|
||
|
|
||
|
func (r *MemoryTTL) isValid(key string) bool {
|
||
|
setAt, ok := r.setAts[key]
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if r.ttl == zeroTTL {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
return setAt.Add(r.ttl).After(time.Now())
|
||
|
}
|