decent tests for scoped flush
This commit is contained in:
Vendored
+2
-51
@@ -2,14 +2,12 @@ package cache
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/umputun/remark/app/rest"
|
||||
)
|
||||
|
||||
// LoadingCache defines interface for caching
|
||||
@@ -50,7 +48,7 @@ type loadingCache struct {
|
||||
}
|
||||
|
||||
// NewLoadingCache makes loadingCache implementation
|
||||
func NewLoadingCache(options ...Option) LoadingCache {
|
||||
func NewLoadingCache(options ...Option) *loadingCache {
|
||||
res := loadingCache{
|
||||
defaultExpiration: time.Hour,
|
||||
cleanupInterval: 5 * time.Minute,
|
||||
@@ -104,6 +102,7 @@ func (lc *loadingCache) Flush(scopes ...string) {
|
||||
|
||||
if len(scopes) == 0 {
|
||||
lc.bytesCache.Flush()
|
||||
lc.withLock(func() { lc.activeKeys = map[string]struct{}{} })
|
||||
go lc.postFlushFn()
|
||||
return
|
||||
}
|
||||
@@ -152,51 +151,3 @@ func (lc *loadingCache) allowed(data []byte) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Option func type
|
||||
type Option func(lc *loadingCache) error
|
||||
|
||||
// MaxValSize functional option defines the largest value's size allowed to be cached
|
||||
// By default it is 0, which means unlimited.
|
||||
func MaxValSize(max int) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.maxValueSize = max
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxKeys functional option defines how many keys to keep.
|
||||
// By default it is 0, which means unlimited.
|
||||
func MaxKeys(max int) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.maxKeys = max
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CleanupInterval functional option defines how often cleanup loop activated.
|
||||
func CleanupInterval(interval time.Duration) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.cleanupInterval = interval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PostFlushFn functional option defines how callback function called after each Flush.
|
||||
func PostFlushFn(postFlushFn func()) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.postFlushFn = postFlushFn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// URLKey gets url from request to use it as cache key
|
||||
// admins will have different keys in order to prevent leak of admin-only data to regular users
|
||||
func URLKey(r *http.Request) string {
|
||||
adminPrefix := "admin!!"
|
||||
key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view
|
||||
if user, err := rest.GetUserInfo(r); err == nil && user.Admin { // make separate cache key for admins
|
||||
key = adminPrefix + key
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
Vendored
+50
@@ -188,7 +188,10 @@ func TestLoadingCache_Scopes(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "value2", string(res))
|
||||
|
||||
assert.Equal(t, 2, len(lc.activeKeys))
|
||||
lc.Flush("s1")
|
||||
assert.Equal(t, 1, len(lc.activeKeys))
|
||||
|
||||
lc.Get(Key("key2", "s2"), time.Minute, func() ([]byte, error) {
|
||||
assert.Fail(t, "should stay")
|
||||
return nil, nil
|
||||
@@ -200,6 +203,53 @@ func TestLoadingCache_Scopes(t *testing.T) {
|
||||
assert.Equal(t, "value-upd", string(res), "was deleted, update")
|
||||
}
|
||||
|
||||
func TestLoadingCache_Flush(t *testing.T) {
|
||||
lc := NewLoadingCache(CleanupInterval(time.Second))
|
||||
|
||||
addToCache := func(key string, scopes ...string) {
|
||||
res, err := lc.Get(key, time.Minute, func() ([]byte, error) {
|
||||
return []byte("value" + key), nil
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "value"+key, string(res))
|
||||
}
|
||||
|
||||
init := func() {
|
||||
lc.Flush()
|
||||
addToCache(Key("key1", "s1", "s2"))
|
||||
addToCache(Key("key2", "s1", "s2", "s3"))
|
||||
addToCache(Key("key3", "s1", "s2", "s3"))
|
||||
addToCache(Key("key4", "s2", "s3"))
|
||||
addToCache(Key("key5", "s2"))
|
||||
addToCache(Key("key6"))
|
||||
addToCache(Key("key7", "s4", "s3"))
|
||||
require.Equal(t, 7, len(lc.activeKeys), "cache init")
|
||||
}
|
||||
|
||||
tbl := []struct {
|
||||
scopes []string
|
||||
left int
|
||||
msg string
|
||||
}{
|
||||
{[]string{}, 0, "full flush, no scopes"},
|
||||
{[]string{"s0"}, 7, "flush wrong scope"},
|
||||
{[]string{"s1"}, 4, "flush s1 scope"},
|
||||
{[]string{"s2", "s1"}, 2, "flush s2+s1 scope"},
|
||||
{[]string{"s1", "s2"}, 2, "flush s1+s2 scope"},
|
||||
{[]string{"s1", "s2", "s4"}, 1, "flush s1+s2+s4 scope"},
|
||||
{[]string{"s1", "s2", "s3"}, 1, "flush s1+s2+s3 scope"},
|
||||
{[]string{"s1", "s2", "ss"}, 2, "flush s1+s2+wrong scope"},
|
||||
}
|
||||
|
||||
for i, tt := range tbl {
|
||||
init()
|
||||
lc.Flush(tt.scopes...)
|
||||
assert.Equal(t, tt.left, len(lc.activeKeys), "keys size, %s #%d", tt.msg, i)
|
||||
assert.Equal(t, tt.left, len(lc.bytesCache.Items()), "items size, %s #%d", tt.msg, i)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadingCache_Keys(t *testing.T) {
|
||||
tbl := []struct {
|
||||
key string
|
||||
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/umputun/remark/app/rest"
|
||||
)
|
||||
|
||||
// Option func type
|
||||
type Option func(lc *loadingCache) error
|
||||
|
||||
// MaxValSize functional option defines the largest value's size allowed to be cached
|
||||
// By default it is 0, which means unlimited.
|
||||
func MaxValSize(max int) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.maxValueSize = max
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxKeys functional option defines how many keys to keep.
|
||||
// By default it is 0, which means unlimited.
|
||||
func MaxKeys(max int) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.maxKeys = max
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CleanupInterval functional option defines how often cleanup loop activated.
|
||||
func CleanupInterval(interval time.Duration) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.cleanupInterval = interval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PostFlushFn functional option defines how callback function called after each Flush.
|
||||
func PostFlushFn(postFlushFn func()) Option {
|
||||
return func(lc *loadingCache) error {
|
||||
lc.postFlushFn = postFlushFn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// URLKey gets url from request to use it as cache key
|
||||
// admins will have different keys in order to prevent leak of admin-only data to regular users
|
||||
func URLKey(r *http.Request) string {
|
||||
adminPrefix := "admin!!"
|
||||
key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view
|
||||
if user, err := rest.GetUserInfo(r); err == nil && user.Admin { // make separate cache key for admins
|
||||
key = adminPrefix + key
|
||||
}
|
||||
return key
|
||||
}
|
||||
Reference in New Issue
Block a user