diff --git a/bigcache.go b/bigcache.go index 7ae1f124..a18a55ed 100644 --- a/bigcache.go +++ b/bigcache.go @@ -123,6 +123,26 @@ func (c *BigCache) Get(key string) ([]byte, error) { return shard.get(key, hashedKey) } +// Get reads entry for the key and if it is not exists +// call second argument callback function to resolve actual value. +func (c *BigCache) GetOrSet(key string, fn func() ([]byte, error)) ([]byte, error) { + entry, err := c.Get(key) + if err == nil { + return entry, nil + } + + if err != ErrEntryNotFound { + return nil, err + } + + entry, err = fn() + if err != nil { + return nil, err + } + + return entry, nil +} + // GetWithInfo reads entry for the key with Response info. // It returns an ErrEntryNotFound when // no entry exists for the given key. diff --git a/bigcache_test.go b/bigcache_test.go index 5cd01de4..4dedea24 100644 --- a/bigcache_test.go +++ b/bigcache_test.go @@ -2,6 +2,7 @@ package bigcache import ( "bytes" + "errors" "fmt" "math" "math/rand" @@ -28,6 +29,39 @@ func TestWriteAndGetOnCache(t *testing.T) { assertEqual(t, value, cachedValue) } +func TestGetOrSetOnCache(t *testing.T) { + t.Parallel() + + // given + cache, _ := NewBigCache(DefaultConfig(5 * time.Second)) + value := []byte("value") + + // when + cachedValue, err := cache.GetOrSet("key", func() ([]byte, error) { + return value, nil + }) + + // then + noError(t, err) + assertEqual(t, value, cachedValue) +} + +func TestGetOrSetOnCacheWithError(t *testing.T) { + t.Parallel() + + // given + cache, _ := NewBigCache(DefaultConfig(5 * time.Second)) + errStub := errors.New("some error") + + // when + _, err := cache.GetOrSet("key", func() ([]byte, error) { + return nil, errStub + }) + + // then + assertEqual(t, errStub, err) +} + func TestAppendAndGetOnCache(t *testing.T) { t.Parallel() diff --git a/examples_test.go b/examples_test.go index ccc513a9..48c3c8e0 100644 --- a/examples_test.go +++ b/examples_test.go @@ -18,6 +18,18 @@ func Example() { // Output: value } +func Example_getOrSet() { + cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute)) + + entry, _ := cache.GetOrSet("my-unique-key", func() ([]byte, error) { + /* Some amount of code for resolving value... */ + return []byte("value"), nil + }) + + fmt.Println(string(entry)) + // Output: value +} + func Example_custom() { // When cache load can be predicted in advance then it is better to use custom initialization // because additional memory allocation can be avoided in that way.