Documentation
¶
Overview ¶
Package lcw adds a thin layer on top of lru and expirable cache providing more limits and common interface. The primary method to get (and set) data to/from the cache is LoadingCache.Get returning stored data for a given key or call provided func to retrieve and store, similar to Guava loading cache. Limits allow max values for key size, number of keys, value size and total size of values in the cache. CacheStat gives general stats on cache performance. 3 flavors of cache provided - NoP (do-nothing cache), ExpirableCache (TTL based), and LruCache
Example (LoadingCacheMutability) ¶
nolint:govet //false positive due to example name ExampleLoadingCacheMutability illustrates changing mutable stored item outside of cache, works only for non-Redis cache.
o := NewOpts[[]string]()
c, err := NewExpirableCache(o.MaxKeys(10), o.TTL(time.Minute*30)) // make expirable cache (30m o.TTL) with up to 10 keys
if err != nil {
panic("can' make cache")
}
defer c.Close()
mutableSlice := []string{"key1", "key2"}
// put mutableSlice in "mutableSlice" cache key
_, _ = c.Get("mutableSlice", func() ([]string, error) {
return mutableSlice, nil
})
// get from cache, func won't run because mutableSlice is cached
// value is original now
v, _ := c.Get("mutableSlice", func() ([]string, error) {
return nil, nil
})
fmt.Printf("got %v slice from cache\n", v)
mutableSlice[0] = "another_key_1"
mutableSlice[1] = "another_key_2"
// get from cache, func won't run because mutableSlice is cached
// value is changed inside the cache now because mutableSlice stored as-is, in mutable state
v, _ = c.Get("mutableSlice", func() ([]string, error) {
return nil, nil
})
fmt.Printf("got %v slice from cache after it's change outside of cache\n", v)
Output: got [key1 key2] slice from cache got [another_key_1 another_key_2] slice from cache after it's change outside of cache
Index ¶
- Constants
- Variables
- type CacheStat
- type ExpirableCache
- func (c *ExpirableCache[V]) Close() error
- func (c *ExpirableCache[V]) Delete(key string)
- func (c *ExpirableCache[V]) Get(key string, fn func() (V, error)) (data V, err error)
- func (c *ExpirableCache[V]) Invalidate(fn func(key string) bool)
- func (c *ExpirableCache[V]) Keys() (res []string)
- func (c *ExpirableCache[V]) Peek(key string) (V, bool)
- func (c *ExpirableCache[V]) Purge()
- func (c *ExpirableCache[V]) Stat() CacheStat
- type FlusherRequest
- type Key
- type LoadingCache
- type LruCache
- func (c *LruCache[V]) Close() error
- func (c *LruCache[V]) Delete(key string)
- func (c *LruCache[V]) Get(key string, fn func() (V, error)) (data V, err error)
- func (c *LruCache[V]) Invalidate(fn func(key string) bool)
- func (c *LruCache[V]) Keys() (res []string)
- func (c *LruCache[V]) Peek(key string) (V, bool)
- func (c *LruCache[V]) Purge()
- func (c *LruCache[V]) Stat() CacheStat
- type Nop
- func (n *Nop[V]) Close() error
- func (n *Nop[V]) Delete(string)
- func (n *Nop[V]) Get(_ string, fn func() (V, error)) (V, error)
- func (n *Nop[V]) Invalidate(func(key string) bool)
- func (n *Nop[V]) Keys() []string
- func (n *Nop[V]) Peek(string) (V, bool)
- func (n *Nop[V]) Purge()
- func (n *Nop[V]) Stat() CacheStat
- type Option
- type RedisCache
- func (c *RedisCache[V]) Close() error
- func (c *RedisCache[V]) Delete(key string)
- func (c *RedisCache[V]) Get(key string, fn func() (V, error)) (data V, err error)
- func (c *RedisCache[V]) Invalidate(fn func(key string) bool)
- func (c *RedisCache[V]) Keys() (res []string)
- func (c *RedisCache[V]) Peek(key string) (data V, found bool)
- func (c *RedisCache[V]) Purge()
- func (c *RedisCache[V]) Stat() CacheStat
- type Scache
- type Sizer
- type WorkerOptions
- func (o *WorkerOptions[V]) EventBus(pubSub eventbus.PubSub) Option[V]
- func (o *WorkerOptions[V]) MaxCacheSize(maximum int64) Option[V]
- func (o *WorkerOptions[V]) MaxKeySize(maximum int) Option[V]
- func (o *WorkerOptions[V]) MaxKeys(maximum int) Option[V]
- func (o *WorkerOptions[V]) MaxValSize(maximum int) Option[V]
- func (o *WorkerOptions[V]) OnEvicted(fn func(key string, value V)) Option[V]
- func (o *WorkerOptions[V]) RedisKeyPrefix(prefix string) Option[V]
- func (o *WorkerOptions[V]) StrToV(fn func(string) V) Option[V]
- func (o *WorkerOptions[V]) TTL(ttl time.Duration) Option[V]
- type Workers
Examples ¶
Constants ¶
const RedisValueSizeLimit = 512 * 1024 * 1024
RedisValueSizeLimit is maximum allowed value size in Redis
Variables ¶
var ErrLoaderPanic = errors.New("cache loader panic")
ErrLoaderPanic is what callers waiting for a load get when the loader panicked. The panic value itself keeps propagating in the goroutine that ran the loader.
Functions ¶
This section is empty.
Types ¶
type ExpirableCache ¶
type ExpirableCache[V any] struct { Workers[V] CacheStat // contains filtered or unexported fields }
ExpirableCache implements LoadingCache with TTL.
func NewExpirableCache ¶
func NewExpirableCache[V any](opts ...Option[V]) (*ExpirableCache[V], error)
NewExpirableCache makes expirable LoadingCache implementation, 1000 max keys by default and 5m TTL. Note that the underlying hashicorp/golang-lru expirable backend calls eviction callbacks while holding its own lock, so an OnEvicted handler must not call back into the same cache, it would deadlock. The same handler is safe to use with LruCache and with the v1 ExpirableCache. Reported upstream as https://github.com/hashicorp/golang-lru/issues/230
func (*ExpirableCache[V]) Close ¶
func (c *ExpirableCache[V]) Close() error
Close supposed to kill cleanup goroutine, but it's not possible before https://github.com/hashicorp/golang-lru/issues/159 is solved so for now it just cleans it.
func (*ExpirableCache[V]) Delete ¶
func (c *ExpirableCache[V]) Delete(key string)
Delete cache item by key
func (*ExpirableCache[V]) Get ¶
func (c *ExpirableCache[V]) Get(key string, fn func() (V, error)) (data V, err error)
Get gets value by key or load with fn if not found in cache
func (*ExpirableCache[V]) Invalidate ¶
func (c *ExpirableCache[V]) Invalidate(fn func(key string) bool)
Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
func (*ExpirableCache[V]) Keys ¶
func (c *ExpirableCache[V]) Keys() (res []string)
Keys returns cache keys
func (*ExpirableCache[V]) Peek ¶
func (c *ExpirableCache[V]) Peek(key string) (V, bool)
Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
func (*ExpirableCache[V]) Purge ¶
func (c *ExpirableCache[V]) Purge()
Purge clears the cache completely.
func (*ExpirableCache[V]) Stat ¶
func (c *ExpirableCache[V]) Stat() CacheStat
Stat returns cache statistics
type FlusherRequest ¶
type FlusherRequest struct {
// contains filtered or unexported fields
}
FlusherRequest used as input for cache.Flush
func Flusher ¶
func Flusher(partition string) FlusherRequest
Flusher makes new FlusherRequest with empty scopes
func (FlusherRequest) Scopes ¶
func (f FlusherRequest) Scopes(scopes ...string) FlusherRequest
Scopes adds scopes to FlusherRequest
type Key ¶
type Key struct {
// contains filtered or unexported fields
}
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 LoadingCache ¶
type LoadingCache[V any] interface { Get(key string, fn func() (V, error)) (val V, err error) // load or get from cache Peek(key string) (V, bool) // get from cache by key Invalidate(fn func(key string) bool) // invalidate items for func(key) == true Delete(key string) // delete by key Purge() // clear cache Stat() CacheStat // cache stats Keys() []string // list of all keys Close() error // close open connections }
LoadingCache defines guava-like cache with Get method returning cached value ao retrieving it if not in cache
type LruCache ¶
LruCache wraps lru.LruCache with loading cache Get and size limits
Example ¶
LruCache illustrates the use of LRU loading cache
// set up test server for single response
var hitCount int
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/post/42" && hitCount == 0 {
_, _ = w.Write([]byte("<html><body>test response</body></html>"))
return
}
w.WriteHeader(404)
}))
// load page function
loadURL := func(url string) (string, error) {
resp, err := http.Get(url) // nolint
if err != nil {
return "", err
}
b, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil {
return "", err
}
return string(b), nil
}
// fixed size LRU cache, 100 items, up to 10k in total size
o := NewOpts[string]()
cache, err := NewLruCache(o.MaxKeys(100), o.MaxCacheSize(10*1024))
if err != nil {
log.Printf("can't make lru cache, %v", err)
}
// url not in cache, load data
url := ts.URL + "/post/42"
val, err := cache.Get(url, func() (val string, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(val)
// url not in cache, load data
val, err = cache.Get(url, func() (val string, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(val)
// url cached, skip load and get from the cache
val, err = cache.Get(url, func() (val string, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(val)
// get cache stats
stats := cache.Stat()
fmt.Printf("%+v\n", stats)
// close test HTTP server after all log.Fatalf are passed
ts.Close()
Output: <html><body>test response</body></html> <html><body>test response</body></html> <html><body>test response</body></html> {hits:2, misses:1, ratio:0.67, keys:1, size:39, errors:0}
func NewLruCache ¶
NewLruCache makes LRU LoadingCache implementation, 1000 max keys by default
Example ¶
ExampleNewLruCache matches the usage example in README.md, keeping it verified.
o := NewOpts[int]()
cache, err := NewLruCache(o.MaxKeys(500), o.MaxCacheSize(65536), o.MaxValSize(200), o.MaxKeySize(32))
if err != nil {
panic("failed to create cache")
}
defer cache.Close()
val, err := cache.Get("key123", func() (int, error) {
return 123, nil // load the value from the actual source here
})
if err != nil {
panic("failed to get data")
}
fmt.Println(val) // cached value
Output: 123
func (*LruCache[V]) Invalidate ¶
Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
type Nop ¶
type Nop[V any] struct{}
Nop is do-nothing implementation of LoadingCache
func (*Nop[V]) Invalidate ¶
Invalidate does nothing for nop cache
type RedisCache ¶
RedisCache implements LoadingCache for Redis.
func NewRedisCache ¶
func NewRedisCache[V any](backend redis.UniversalClient, opts ...Option[V]) (*RedisCache[V], error)
NewRedisCache makes Redis LoadingCache implementation. Supports only string and string-based types and will return error otherwise. Without RedisKeyPrefix the cache assumes exclusive ownership of the selected Redis database, i.e. Purge flushes it entirely and Keys, Stat and MaxKeys count every key in it. MaxCacheSize is not supported by this backend, it is accepted but ignored, and Stat reports size 0.
func (*RedisCache[V]) Close ¶
func (c *RedisCache[V]) Close() error
Close closes underlying connections
func (*RedisCache[V]) Get ¶
func (c *RedisCache[V]) Get(key string, fn func() (V, error)) (data V, err error)
Get gets value by key or load with fn if not found in cache
func (*RedisCache[V]) Invalidate ¶
func (c *RedisCache[V]) Invalidate(fn func(key string) bool)
Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
func (*RedisCache[V]) Keys ¶
func (c *RedisCache[V]) Keys() (res []string)
Keys gets all keys for the cache
func (*RedisCache[V]) Peek ¶
func (c *RedisCache[V]) Peek(key string) (data V, found bool)
Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
func (*RedisCache[V]) Purge ¶
func (c *RedisCache[V]) Purge()
Purge clears the cache completely. Without RedisKeyPrefix set it flushes the whole redis database.
type Scache ¶
type Scache[V any] struct { // contains filtered or unexported fields }
Scache wraps LoadingCache with partitions (sub-system), and scopes. Simplified interface with just 4 funcs - Get, Flush, Stats and Close
Example ¶
LruCache illustrates the use of LRU loading cache
// set up test server for single response
var hitCount int
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/post/42" && hitCount == 0 {
_, _ = w.Write([]byte("<html><body>test response</body></html>"))
return
}
w.WriteHeader(404)
}))
// load page function
loadURL := func(url string) ([]byte, error) {
resp, err := http.Get(url) // nolint
if err != nil {
return nil, err
}
b, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil {
return nil, err
}
return b, nil
}
// fixed size LRU cache, 100 items, up to 10k in total size
o := NewOpts[[]byte]()
backend, err := NewLruCache(o.MaxKeys(100), o.MaxCacheSize(10*1024))
if err != nil {
log.Fatalf("can't make lru cache, %v", err)
}
cache := NewScache[[]byte](backend)
// url not in cache, load data
url := ts.URL + "/post/42"
key := NewKey().ID(url).Scopes("test")
val, err := cache.Get(key, func() (val []byte, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(string(val))
// url not in cache, load data
key = NewKey().ID(url).Scopes("test")
val, err = cache.Get(key, func() (val []byte, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(string(val))
// url cached, skip load and get from the cache
key = NewKey().ID(url).Scopes("test")
val, err = cache.Get(key, func() (val []byte, err error) {
return loadURL(url)
})
if err != nil {
log.Fatalf("can't load url %s, %v", url, err)
}
fmt.Println(string(val))
// get cache stats
stats := cache.Stat()
fmt.Printf("%+v\n", stats)
// close cache and test HTTP server after all log.Fatalf are passed
ts.Close()
err = cache.Close()
if err != nil {
log.Fatalf("can't close cache %v", err)
}
Output: <html><body>test response</body></html> <html><body>test response</body></html> <html><body>test response</body></html> {hits:2, misses:1, ratio:0.67, keys:1, size:39, errors:0}
func NewScache ¶
func NewScache[V any](lc LoadingCache[V]) *Scache[V]
NewScache creates Scache on top of LoadingCache
func (*Scache[V]) Flush ¶
func (m *Scache[V]) Flush(req FlusherRequest)
Flush clears keys of the requested partition, matching the requested scopes. With no scopes set every key of the partition is removed, keys of other partitions are kept.
type Sizer ¶
type Sizer interface {
Size() int
}
Sizer allows to perform size-based restrictions, optional. Values implementing it define their own size, []byte and string are sized by length. For any other type both maxValueSize and maxCacheSize checks are ignored.
type WorkerOptions ¶
type WorkerOptions[T any] struct{}
WorkerOptions holds the option setting methods
func (*WorkerOptions[V]) EventBus ¶
func (o *WorkerOptions[V]) EventBus(pubSub eventbus.PubSub) Option[V]
EventBus sets PubSub for distributed cache invalidation
func (*WorkerOptions[V]) MaxCacheSize ¶
func (o *WorkerOptions[V]) MaxCacheSize(maximum int64) Option[V]
MaxCacheSize functional option defines the total size of cached data. Applies to values implementing Sizer as well as []byte and string, other types are not counted. Not supported by RedisCache, which accepts the option but ignores it. By default, it is 0, which means unlimited.
func (*WorkerOptions[V]) MaxKeySize ¶
func (o *WorkerOptions[V]) MaxKeySize(maximum int) Option[V]
MaxKeySize functional option defines the largest key's size allowed to be used in cache By default it is 0, which means unlimited.
func (*WorkerOptions[V]) MaxKeys ¶
func (o *WorkerOptions[V]) MaxKeys(maximum int) Option[V]
MaxKeys functional option defines how many keys to keep. By default, it is 0, which means unlimited.
func (*WorkerOptions[V]) MaxValSize ¶
func (o *WorkerOptions[V]) MaxValSize(maximum int) Option[V]
MaxValSize functional option defines the largest value's size allowed to be cached. Applies to values implementing Sizer as well as []byte and string, other types are not limited. By default it is 0, which means unlimited.
func (*WorkerOptions[V]) OnEvicted ¶
func (o *WorkerOptions[V]) OnEvicted(fn func(key string, value V)) Option[V]
OnEvicted sets callback on invalidation event. The callback runs outside the cache lock for LruCache, but ExpirableCache calls it while its backend lock is held, so with ExpirableCache the handler must not call back into the same cache.
func (*WorkerOptions[V]) RedisKeyPrefix ¶ added in v2.1.0
func (o *WorkerOptions[V]) RedisKeyPrefix(prefix string) Option[V]
RedisKeyPrefix sets the prefix for all keys stored in redis, making the cache use only its own namespace instead of the whole redis database. Applies to RedisCache only, ignored by other backends. Empty by default, which means the cache assumes exclusive ownership of the selected redis database. Note that with a prefix set and MaxKeys defined, key counting scans the keyspace on every miss.
func (*WorkerOptions[V]) StrToV ¶
func (o *WorkerOptions[V]) StrToV(fn func(string) V) Option[V]
StrToV sets strToV function for RedisCache
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package eventbus provides PubSub interface used for distributed cache invalidation, as well as NopPubSub and RedisPubSub implementations.
|
Package eventbus provides PubSub interface used for distributed cache invalidation, as well as NopPubSub and RedisPubSub implementations. |