Add SetNx (Set if not exist) method to cache
- addressng the issue : https://github.com/patrickmn/go-cache/issues/59
This commit is contained in:
parent
a3647f8e31
commit
86c719bb7c
|
@ -75,6 +75,12 @@ func main() {
|
|||
foo := x.(*MyStruct)
|
||||
// ...
|
||||
}
|
||||
|
||||
// Set new value if the item is missing or expired
|
||||
item, found := c.SetNx("foo", cache.DefaultExpiration, func(s string) (interface{}, error) {
|
||||
// return value to set if cache miss
|
||||
return "bar", nil
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
|
19
cache.go
19
cache.go
|
@ -135,6 +135,25 @@ func (c *cache) Get(k string) (interface{}, bool) {
|
|||
return item.Object, true
|
||||
}
|
||||
|
||||
func (c *cache) SetNx(k string, expiry time.Duration, funcToCall func(string) (interface{}, error)) (interface{},
|
||||
bool) {
|
||||
c.mu.RLock()
|
||||
item, found := c.items[k]
|
||||
if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration){
|
||||
val, err := funcToCall(k)
|
||||
if err != nil {
|
||||
c.mu.RUnlock()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
c.set(k, val, expiry)
|
||||
c.mu.RUnlock()
|
||||
return val, true
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
return item.Object, true
|
||||
}
|
||||
|
||||
// GetWithExpiration returns an item and its expiration time from the cache.
|
||||
// It returns the item or nil, the expiration time if one is set (if the item
|
||||
// never expires a zero value for time.Time is returned), and a bool indicating
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type TestStruct struct {
|
||||
|
@ -1769,3 +1771,51 @@ func TestGetWithExpiration(t *testing.T) {
|
|||
t.Error("expiration for e is in the past")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNx(t *testing.T) {
|
||||
tc := New(DefaultExpiration, 0)
|
||||
val1, isPresent := tc.SetNx("key1", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return "a for apple", nil
|
||||
})
|
||||
|
||||
assert.True(t, isPresent)
|
||||
assert.Equal(t, "a for apple", val1)
|
||||
|
||||
val2, isPresent := tc.SetNx("key2", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return 10, nil
|
||||
})
|
||||
|
||||
assert.True(t, isPresent)
|
||||
assert.Equal(t, 10, val2)
|
||||
|
||||
tc.set("existingKey", []int{1,2,3}, 10 * time.Second)
|
||||
val3, isPresent := tc.SetNx("existingKey", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return []int{4}, nil
|
||||
})
|
||||
|
||||
assert.True(t, isPresent)
|
||||
assert.Equal(t, []int{1,2,3}, val3)
|
||||
|
||||
tc.set("expiredKey", "expiredValue", 10 * time.Millisecond)
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
val4, isPresent := tc.SetNx("expiredKey", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return "newValue", nil
|
||||
})
|
||||
|
||||
assert.True(t, isPresent)
|
||||
assert.Equal(t, "newValue", val4)
|
||||
|
||||
val5, isPresent := tc.SetNx("errorKey", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return "doesn't matter return value", errors.New("some error")
|
||||
})
|
||||
|
||||
assert.False(t, isPresent)
|
||||
assert.Nil(t, val5)
|
||||
|
||||
val6, isPresent := tc.SetNx("errorKey", 10 * time.Second, func(s string) (interface{}, error) {
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
assert.True(t, isPresent)
|
||||
assert.Nil(t, val6)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue