diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e35c36 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# Simple TTL Cache +The Simple TTL Cache is an in memory cache with a set expiration time (TTL). + +# Usage +```go +package main + +import ( + "time" + + simplettlcache "github.com/GuySWatson/simple-ttl-cache" +) + +func main() { + // Setup + simpleTTLCache := simplettlcache.SimpleTTLCache{} + simpleTTLCache.Init(time.Second * 3) + + // Put object into cache + simpleTTLCache.Put("color", "red") + + // Get object out of cache + simpleTTLCache.Get("color") + + // Update object in the cache + simpleTTLCache.Update("color", 1234) +} +``` \ No newline at end of file diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..95d3af0 --- /dev/null +++ b/cache.go @@ -0,0 +1,78 @@ +package simplettlcache + +import ( + "sync" + "time" +) + +type SimpleTTLCache struct { + assets map[string]*simpleTTLItem + mutex sync.Mutex +} + +type simpleTTLItem struct { + value interface{} + lastAccess time.Time +} + +func (s *SimpleTTLCache) Init(maxTTL time.Duration) { + s.assets = make(map[string]*simpleTTLItem) + + go func() { + for now := range time.Tick(time.Second) { + s.mutex.Lock() + + for k, v := range s.assets { + if now.Sub(v.lastAccess) > maxTTL { + delete(s.assets, k) + } + } + + s.mutex.Unlock() + } + }() +} + +func (s *SimpleTTLCache) Len() int { + return len(s.assets) +} + +func (s *SimpleTTLCache) Put(k string, value interface{}) { + s.mutex.Lock() + + item, ok := s.assets[k] + if !ok { + item = &simpleTTLItem{ + value: value, + } + s.assets[k] = item + } + + item.lastAccess = time.Now() + + s.mutex.Unlock() +} + +func (s *SimpleTTLCache) Get(k string) (v interface{}) { + s.mutex.Lock() + + if item, ok := s.assets[k]; ok { + v = item.value + item.lastAccess = time.Now() + } + + s.mutex.Unlock() + return +} + +func (s *SimpleTTLCache) Update(k string, value interface{}) { + s.mutex.Lock() + + item, ok := s.assets[k] + if ok { + item.value = value + item.lastAccess = time.Now() + } + + s.mutex.Unlock() +} diff --git a/cache_test.go b/cache_test.go new file mode 100644 index 0000000..37b7f1f --- /dev/null +++ b/cache_test.go @@ -0,0 +1,60 @@ +package simplettlcache + +import ( + "testing" + "time" +) + +func TestSimpleTTLCache(t *testing.T) { + key := "a1" + + // Setup cache for test + simpleTTLCache := SimpleTTLCache{} + simpleTTLCache.Init(time.Second * 3) + + // Put item in cache and check it exists + simpleTTLCache.Put(key, "red") + if simpleTTLCache.Get(key) != "red" { + t.Error("Item missing from cache") + } + + // Sleep to test TTL works + time.Sleep(time.Second * 4) + if simpleTTLCache.Get(key) != nil { + t.Error("Should be empty") + } +} + +func TestSimpleTTLCacheLen(t *testing.T) { + key := "b2" + + // Setup cache for test + simpleTTLCache := SimpleTTLCache{} + simpleTTLCache.Init(time.Second * 3) + + // Put item in cache and check length + simpleTTLCache.Put(key, "apple") + if simpleTTLCache.Len() != 1 { + t.Error("Len of cache is wrong") + } +} + +func TestSimpleTTLCacheUpdate(t *testing.T) { + key := "b2" + + // Setup cache for test + simpleTTLCache := SimpleTTLCache{} + simpleTTLCache.Init(time.Second * 3) + + // Put item in cache and check it exists + simpleTTLCache.Put(key, "apple") + if simpleTTLCache.Get(key) != "apple" { + t.Error("Item missing from cache") + } + + // Update item in cache and check it changed + simpleTTLCache.Update(key, "banana") + if simpleTTLCache.Get(key) != "banana" { + t.Error("Item update failed") + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..35dda56 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/GuySWatson/simple-ttl-cache + +go 1.15