decent tests for scoped flush

This commit is contained in:
Umputun
2018-05-25 17:55:45 -05:00
parent 22c866120f
commit 98211229d3
3 changed files with 109 additions and 51 deletions
+2 -51
View File
@@ -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
}
+50
View File
@@ -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
+57
View File
@@ -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
}