Skip to content

Commit adfc258

Browse files
authored
Merge pull request #272 from advoretsky/pr271_prevent_double_calls
move whole logic of Get under singleFlight #271
2 parents da5a8aa + cc5e893 commit adfc258

File tree

2 files changed

+38
-40
lines changed

2 files changed

+38
-40
lines changed

lib/cache/loadable.go

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package cache
33
import (
44
"context"
55
"errors"
6+
"fmt"
7+
"reflect"
68
"sync"
79

810
"github.com/eko/gocache/lib/v4/store"
@@ -55,56 +57,54 @@ func (c *LoadableCache[T]) setter() {
5557
c.Set(context.Background(), item.key, item.value, item.options...)
5658

5759
cacheKey := c.getCacheKey(item.key)
58-
c.singleFlight.Forget(cacheKey)
5960
c.setCache.Delete(cacheKey)
6061
}
6162
}
6263

6364
// Get returns the object stored in cache if it exists
6465
func (c *LoadableCache[T]) Get(ctx context.Context, key any) (T, error) {
65-
var err error
66-
67-
object, err := c.cache.Get(ctx, key)
68-
if err == nil {
69-
return object, err
70-
}
71-
72-
// Unable to find in cache, try to load it from load function
7366
cacheKey := c.getCacheKey(key)
74-
if v, ok := c.setCache.Load(cacheKey); ok {
75-
return v.(T), nil
76-
}
77-
78-
zero := *new(T)
79-
80-
rawLoadedResult, err, _ := c.singleFlight.Do(
67+
if value, err, _ := c.singleFlight.Do(
8168
cacheKey,
8269
func() (any, error) {
83-
value, options, innerErr := c.loadFunc(ctx, key)
84-
85-
return &loadableKeyValue[T]{
86-
key: key,
87-
value: value,
88-
options: options,
89-
}, innerErr
70+
// try temporary-while-setter-works cache
71+
if v, ok := c.setCache.Load(cacheKey); ok {
72+
return v, nil
73+
}
74+
// try main cache
75+
if v, err := c.cache.Get(ctx, key); err == nil {
76+
return v, err
77+
}
78+
// Unable to find in cache, try to load it from load function
79+
if value, options, err := c.loadFunc(ctx, key); err == nil {
80+
81+
// cache locally until main cache is set
82+
c.setCache.Store(cacheKey, value)
83+
84+
c.setChannel <- &loadableKeyValue[T]{
85+
key: key,
86+
value: value,
87+
options: options,
88+
}
89+
return value, err
90+
} else {
91+
return *new(T), err
92+
}
9093
},
91-
)
92-
if err != nil {
93-
return zero, err
94-
}
95-
96-
loadedKeyValue, ok := rawLoadedResult.(*loadableKeyValue[T])
97-
if !ok {
94+
); err != nil {
95+
return *new(T), err
96+
} else if value, ok := value.(T); ok {
97+
return value, err
98+
} else {
99+
zero := *new(T)
98100
return zero, errors.New(
99-
"returned value can't be cast to *loadableKeyValue[T]",
101+
fmt.Sprintf(
102+
"type assertion failed: expected %s, got %s",
103+
reflect.TypeOf(zero),
104+
reflect.TypeOf(value),
105+
),
100106
)
101107
}
102-
103-
// Then, put it back in cache
104-
c.setCache.Store(cacheKey, loadedKeyValue.value)
105-
c.setChannel <- loadedKeyValue
106-
107-
return loadedKeyValue.value, err
108108
}
109109

110110
// Set sets a value in available caches

lib/cache/loadable_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ func TestLoadableGetWhenAvailableInLoadFunc(t *testing.T) {
101101
// Cache 1
102102
cache1 := NewMockSetterCacheInterface[any](ctrl)
103103
cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
104-
cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
105-
cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
106104
cache1.EXPECT().Set(ctx, "my-key", cacheValue).AnyTimes().Return(nil)
107105

108106
var loadCallCount int32
@@ -315,7 +313,7 @@ func TestLoadableGetTwice(t *testing.T) {
315313
cache := NewLoadable[any](loadFunc, cache1)
316314

317315
key := 1
318-
cache1.EXPECT().Get(context.Background(), key).Return(nil, store.NotFound{}).Times(2)
316+
cache1.EXPECT().Get(context.Background(), key).Return(nil, store.NotFound{}).AnyTimes()
319317
cache1.EXPECT().Set(context.Background(), key, uint64(1)).Times(1)
320318
v1, err1 := cache.Get(context.Background(), key)
321319
v2, err2 := cache.Get(context.Background(), key) // setter may not be called now because it's done by another goroutine

0 commit comments

Comments
 (0)