From fa22f63111bd47535802cf6d848646aa48df39ab Mon Sep 17 00:00:00 2001 From: Rene Kroon Date: Sat, 22 May 2021 20:28:41 +0200 Subject: [PATCH] Split Get with Loader function, introduce SimpleCache --- Readme.md | 33 ++++++++++++++++++++++++++++++++- cache.go | 21 ++++++++++++++++++--- cache_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 08ea517..2ab17e3 100644 --- a/Readme.md +++ b/Readme.md @@ -23,8 +23,39 @@ Note (issue #25): by default, due to historic reasons, the TTL will be reset on ## Usage -You can copy it as a full standalone demo program. +You can copy it as a full standalone demo program. The first snippet is basic usage, where the second exploits more options in the cache. +Basic: +```go +package main + +import ( + "fmt" + "time" + + "github.com/ReneKroon/ttlcache/v2" +) + +var notFound = ttlcache.ErrNotFound + +func main() { + var cache ttlcache.SimpleCache = ttlcache.NewCache() + + cache.SetTTL(time.Duration(10 * time.Second)) + cache.Set("MyKey", "MyValue") + cache.Set("MyNumber", 1000) + + if val, err := cache.Get("MyKey"); err != notFound { + fmt.Printf("Got it: %s\n", val) + } + + cache.Remove("MyNumber") + cache.Purge() + cache.Close() +} +``` + +Advanced: ```go package main diff --git a/cache.go b/cache.go index ad620a8..a3e32e9 100644 --- a/cache.go +++ b/cache.go @@ -18,6 +18,17 @@ type ExpireReasonCallback func(key string, reason EvictionReason, value interfac // LoaderFunction can be supplied to retrieve an item where a cache miss occurs. Supply an item specific ttl or Duration.Zero type LoaderFunction func(key string) (data interface{}, ttl time.Duration, err error) +// SimpleCache interface enables a quick-start. Interface for basic usage. +type SimpleCache interface { + Get(key string) (interface{}, error) + Set(key string, data interface{}) error + SetTTL(ttl time.Duration) error + SetWithTTL(key string, data interface{}, ttl time.Duration) error + Remove(key string) error + Close() error + Purge() error +} + // Cache is a synchronized map of items that can auto-expire once stale type Cache struct { mutex sync.Mutex @@ -263,9 +274,13 @@ func (cache *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) return nil } +func (cache *Cache) Get(key string) (interface{}, error) { + return cache.GetByLoader(key, nil) +} + // Get is a thread-safe way to lookup items // Every lookup, also touches the item, hence extending it's life -func (cache *Cache) Get(key string, customLoaderFunction ...LoaderFunction) (interface{}, error) { +func (cache *Cache) GetByLoader(key string, customLoaderFunction LoaderFunction) (interface{}, error) { cache.mutex.Lock() if cache.isShutDown { cache.mutex.Unlock() @@ -288,8 +303,8 @@ func (cache *Cache) Get(key string, customLoaderFunction ...LoaderFunction) (int } loaderFunction := cache.loaderFunction - if len(customLoaderFunction) > 0 { - loaderFunction = customLoaderFunction[0] + if customLoaderFunction != nil { + loaderFunction = customLoaderFunction } if loaderFunction == nil || exists { diff --git a/cache_test.go b/cache_test.go index 6e770c4..e572487 100644 --- a/cache_test.go +++ b/cache_test.go @@ -20,6 +20,57 @@ func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } +// The SimpleCache interface enables quick-start. +func TestCache_SimpleCache(t *testing.T) { + t.Parallel() + var cache SimpleCache = NewCache() + + cache.SetTTL(time.Second) + cache.Set("k", "v") + cache.Get("k") + cache.Purge() + cache.Close() + +} + +// Issue / PR #39: add customer loader function for each Get() # +// some middleware prefers to define specific context's etc per Get. +// This is faciliated by supplying a loder function with Get's. +func TestCache_GetByLoader(t *testing.T) { + t.Parallel() + cache := NewCache() + defer cache.Close() + + globalLoader := func(key string) (data interface{}, ttl time.Duration, err error) { + return "global", 0, nil + } + cache.SetLoaderFunction(globalLoader) + + localLoader := func(key string) (data interface{}, ttl time.Duration, err error) { + return "local", 0, nil + } + + key, _ := cache.Get("test") + assert.Equal(t, "global", key) + + cache.Remove("test") + + localKey, _ := cache.GetByLoader("test", localLoader) + assert.Equal(t, "local", localKey) + + cache.Remove("test") + + globalKey, _ := cache.GetByLoader("test", globalLoader) + assert.Equal(t, "global", globalKey) + + cache.Remove("test") + + defaultKey, _ := cache.GetByLoader("test", nil) + assert.Equal(t, "global", defaultKey) + + cache.Remove("test") +} + // Issue #38: Feature request: ability to know why an expiry has occurred func TestCache_textExpirationReasons(t *testing.T) { t.Parallel()