Files
remark42/backend/vendor/github.com/go-pkgz/lcw/scache.go
T
Dmitry Verkhoturov 9ad3be2e97 bump go modules, make auth dev hostname customisable
After this commit, dev auth would start working with the `REMARK_URL`
hostname instead of the previously hardcoded 127.0.0.1.

Breaks development setup where `REMARK_URL` was set
to a non-standard value and dev auth was running on 127.0.0.1
and working, as, after that change, it would stop working.
2022-08-26 23:35:57 +02:00

146 lines
3.5 KiB
Go

package lcw
import (
"fmt"
"strings"
)
// Scache wraps LoadingCache with partitions (sub-system), and scopes.
// Simplified interface with just 4 funcs - Get, Flush, Stats and Close
type Scache struct {
lc LoadingCache
}
// NewScache creates Scache on top of LoadingCache
func NewScache(lc LoadingCache) *Scache {
return &Scache{lc: lc}
}
// Get retrieves a key from underlying backend
func (m *Scache) Get(key Key, fn func() ([]byte, error)) (data []byte, err error) {
keyStr := key.String()
val, err := m.lc.Get(keyStr, func() (value interface{}, e error) {
return fn()
})
return val.([]byte), err
}
// Stat delegates the call to the underlying cache backend
func (m *Scache) Stat() CacheStat {
return m.lc.Stat()
}
// Close calls Close function of the underlying cache
func (m *Scache) Close() error {
return m.lc.Close()
}
// Flush clears cache and calls postFlushFn async
func (m *Scache) Flush(req FlusherRequest) {
if len(req.scopes) == 0 {
m.lc.Purge()
return
}
// check if fullKey has matching scopes
inScope := func(fullKey string) bool {
key, err := parseKey(fullKey)
if err != nil {
return false
}
for _, s := range req.scopes {
for _, ks := range key.scopes {
if ks == s {
return true
}
}
}
return false
}
for _, k := range m.lc.Keys() {
if inScope(k) {
m.lc.Delete(k) // Keys() returns copy of cache's key, safe to remove directly
}
}
}
// Key for scoped cache. Created foe given partition (can be empty) and set with ID and Scopes.
// example: k := NewKey("sys1").ID(postID).Scopes("last_posts", customer_id)
type Key struct {
id string // the primary part of the key, i.e. usual cache's key
partition string // optional id for a subsystem or cache partition
scopes []string // list of scopes to use in invalidation
}
// NewKey makes base key for given partition. Partition can be omitted.
func NewKey(partition ...string) Key {
if len(partition) == 0 {
return Key{partition: ""}
}
return Key{partition: partition[0]}
}
// ID sets key id
func (k Key) ID(id string) Key {
k.id = id
return k
}
// Scopes of the key
func (k Key) Scopes(scopes ...string) Key {
k.scopes = scopes
return k
}
// String makes full string key from primary key, partition and scopes
// key string made as <partition>@@<id>@@<scope1>$$<scope2>....
func (k Key) String() string {
bld := strings.Builder{}
_, _ = bld.WriteString(k.partition)
_, _ = bld.WriteString("@@")
_, _ = bld.WriteString(k.id)
_, _ = bld.WriteString("@@")
_, _ = bld.WriteString(strings.Join(k.scopes, "$$"))
return bld.String()
}
// parseKey gets compound key string created by Key func and split it to the actual key, partition and scopes
// key string made as <partition>@@<id>@@<scope1>$$<scope2>....
func parseKey(keyStr string) (Key, error) {
elems := strings.Split(keyStr, "@@")
if len(elems) != 3 {
return Key{}, fmt.Errorf("can't parse cache key %s, invalid number of segments %d", keyStr, len(elems))
}
scopes := strings.Split(elems[2], "$$")
if len(scopes) == 1 && scopes[0] == "" {
scopes = []string{}
}
key := Key{
partition: elems[0],
id: elems[1],
scopes: scopes,
}
return key, nil
}
// FlusherRequest used as input for cache.Flush
type FlusherRequest struct {
partition string
scopes []string
}
// Flusher makes new FlusherRequest with empty scopes
func Flusher(partition string) FlusherRequest {
res := FlusherRequest{partition: partition}
return res
}
// Scopes adds scopes to FlusherRequest
func (f FlusherRequest) Scopes(scopes ...string) FlusherRequest {
f.scopes = scopes
return f
}