diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba8bd4..a38c33d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 2.4.0 (May 2021) + +## API changes: + +* #42 : Add option to get list of keys +* #40: Allow 'Touch' on items without other operation + +// Touch resets the TTL of the key when it exists, returns ErrNotFound if the key is not present. +func (cache *Cache) Touch(key string) error + +// GetKeys returns all keys of items in the cache. Returns nil when the cache has been closed. +func (cache *Cache) GetKeys() []string + # 2.3.0 (February 2021) ## API changes: diff --git a/Readme.md b/Readme.md index 08ea517..2ab17e3 100644 --- a/Readme.md +++ b/Readme.md @@ -23,8 +23,39 @@ ## 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 51ff261..e433dc9 100644 --- a/cache.go +++ b/cache.go @@ -17,6 +17,17 @@ // 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 { @@ -266,6 +277,11 @@ // 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) (interface{}, error) { + return cache.GetByLoader(key, nil) +} + +// GetByLoader can take a per key loader function (ie. to propagate context) +func (cache *Cache) GetByLoader(key string, customLoaderFunction LoaderFunction) (interface{}, error) { cache.mutex.Lock() if cache.isShutDown { cache.mutex.Unlock() @@ -286,11 +302,17 @@ cache.metrics.Misses++ err = ErrNotFound } - if cache.loaderFunction == nil || exists { - cache.mutex.Unlock() - } - - if cache.loaderFunction != nil && !exists { + + loaderFunction := cache.loaderFunction + if customLoaderFunction != nil { + loaderFunction = customLoaderFunction + } + + if loaderFunction == nil || exists { + cache.mutex.Unlock() + } + + if loaderFunction != nil && !exists { if lock, ok := cache.loaderLock[key]; ok { // if a lock is present then a fetch is in progress and we wait. cache.mutex.Unlock() @@ -310,7 +332,7 @@ cache.loaderLock[key] = m cache.mutex.Unlock() // cache is not blocked during IO - dataToReturn, err = cache.invokeLoader(key) + dataToReturn, err = cache.invokeLoader(key, loaderFunction) cache.mutex.Lock() m.Broadcast() // cleanup so that we don't block consecutive access. @@ -327,10 +349,10 @@ return dataToReturn, err } -func (cache *Cache) invokeLoader(key string) (dataToReturn interface{}, err error) { +func (cache *Cache) invokeLoader(key string, loaderFunction LoaderFunction) (dataToReturn interface{}, err error) { var ttl time.Duration - dataToReturn, ttl, err = cache.loaderFunction(key) + dataToReturn, ttl, err = loaderFunction(key) if err == nil { err = cache.SetWithTTL(key, dataToReturn, ttl) if err != nil { diff --git a/cache_test.go b/cache_test.go index 6e770c4..e572487 100644 --- a/cache_test.go +++ b/cache_test.go @@ -18,6 +18,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 diff --git a/debian/changelog b/debian/changelog index 343b151..9538f94 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +golang-github-renekroon-ttlcache (2.5.0-1) UNRELEASED; urgency=low + + * New upstream release. + + -- Debian Janitor Tue, 25 May 2021 00:59:27 -0000 + golang-github-renekroon-ttlcache (2.3.0+ds-1) unstable; urgency=medium * New upstream release.