English | 中文
tRPC-Go localcache plugin

localcache is a standalone local K-V cache component that allows multiple goroutines to access it concurrently and supports LRU and expiration time based elimination policy.
After the capacity of localcache reaches the upper limit, it will carry out data elimination based on LRU, and the deletion of expired key-value is realized based on time wheel.
applies to readcache scenarios, not to writecache scenarios.
Quick Start
Use the functions directly from the localcache package.
package main
import (
"trpc.group/trpc-go/trpc-database/localcache"
)
func LoadData(ctx context.Context, key string) (interface{}, error) {
return "cat", nil
}
func main() {
localcache.Set("foo", "bar", 5)
value, found := localcache.Get("foo")
value, err := localcache.GetWithLoad(context.TODO(), "tom", LoadData, 5)
localcache.Del("foo")
localcache.Clear()
}
Configuring Usage
New() generates a Cache instance and calls the functional functions of that instance
Optional parameters
WithCapacity(capacity int)
Sets the maximum size of the cache, with a minimum value of 1 and a maximum value of 1e30. When the cache is full, the last element of the queue is eliminated based on LRU. Default value is 1e30.
WithExpiration(ttl int64)
Sets the expiration time of the element in seconds. The default value is 60 seconds.
WithLoad(f LoadFunc)
type LoadFunc func(ctx context.Context, key string) (interface{}, error)
Set data load function, when the key does not exist in cache, use this function to load the corresponding value, and cache it in cache. Use with GetWithLoad().
WithMLoad(f MLoadFunc)
type MLoadFunc func(ctx context.Context, keys []string) (map[string]interface{}, error)
Set bulk data loading function, use this function to bulk load keys that don't exist in cache. Use with MGetWithLoad().
WithDelay(duration int64)
Sets the interval in seconds for delayed deletion of expired keys in cache. The default value is 0, the key is deleted immediately after it expires.
Usage Scenario: When the key expires, at the same time the data downstream service exception of the business, I hope to be able to get the expired value from the cache in the business as the backing data.
WithOnDel(delCallBack ItemCallBackFunc)
type ItemCallBackFunc func(item *Item)
Setting the callback function when an element is deleted: expired deletions, active deletions, and LRU deletions all trigger this callback function.
WithOnExpire(expireCallback ItemCallbackFunc)
type ItemCallBackFunc func(item *Item)
Set the callback function when an element expires. Two callback functions are triggered when an element expires: the expiration callback and the deletion callback.
Cache Interface
type Cache interface {
Get(key string) (interface{}, bool)
GetWithLoad(ctx context.Context, key string) (interface{}, error)
MGetWithLoad(ctx context.Context, keys []string) (map[string]interface{}, error)
GetWithCustomLoad(ctx context.Context, key string, customLoad LoadFunc, ttl int64) (interface{}, error)
MGetWithCustomLoad(ctx context.Context, keys []string, customLoad MLoadFunc, ttl int64) (map[string]interface{}, error)
Set(key string, value interface{}) bool
SetWithExpire(key string, value interface{}, ttl int64) bool
Del(key string)
Clear()
Close()
}
Example
Setting capacity and expiration time
func main() {
var lc localcache.Cache
lc = localcache.New(localcache.WithCapacity(100), localcache.WithExpiration(5))
lc.Set("foo", "bar")
lc.SetWithExpire("tom", "cat", 10)
time.Sleep(time.Millisecond)
val, found := lc.Get("foo")
lc.Del("foo")
}
Delayed deletion
func main() {
lc := localcache.New(localcache.WithDelay(3))
lc.SetWithExpire("tom", "cat", 1)
time.Sleep(time.Second * 2)
value, ok := lc.Get("tom")
if !ok {
fmt.Printf("key:%s is expired or empty\n", "tom")
}
if s, ok := value.(string); ok && s == "cat" {
fmt.Printf("get expired value: %s\n", "cat")
}
}
Custom Load Functions
Set up a custom data loading function and use GetWithLoad(key) to get the value
func main() {
loadFunc := func(ctx context.Context, key string) (interface{}, error) {
return "cat", nil
}
lc := localcache.New(localcache.WithLoad(loadFunc), localcache.WithExpiration(5))
val, err := lc.GetWithLoad(context.TODO(), "tom")
otherLoadFunc := func(ctx context.Context, key string) (interface{}, error) {
return "dog", nil
}
val,err := lc.GetWithCustomLoad(context.TODO(),"tom",otherLoadFunc,10)
}
Set the bulk data load function and use MGetWithLoad(keys) to get values
func main() {
mLoadFunc := func(ctx context.Context, keys []string) (map[string]interface{}, error) {
return map[string]interface{} {
"foo": "bar",
"tom": "cat",
}, nil
}
lc := localcache.New(localcache.WithMLoad(mLoadFunc), localcache.WithExpiration(5))
val, err := lc.MGetWithLoad(context.TODO(), []string{"foo", "tom"})
val,err := lc.MGetWithCustomLoad(context.TODO(),"tom",mLoadFunc,10)
}
Customizing the callback on expiration/deletion of deleted elements
func main() {
delCount := map[string]int{"A": 0, "B": 0, "": 0}
expireCount := map[string]int{"A": 0, "B": 0, "": 0}
c := localcache.New(
localcache.WithCapacity(4),
localcache.WithOnExpire(func(item *localcache.Item) {
if item.Flag != localcache.ItemDelete {
return
}
if _, ok := expireCount[item.Key]; ok {
expireCount[item.Key]++
}
}),
localcache.WithOnDel(func(item *localcache.Item) {
if item.Flag != localcache.ItemDelete {
return
}
if _, ok := delCount[item.Key]; ok {
delCount[item.Key]++
}
}))
defer c.Close()
elems := []struct {
key string
val string
ttl int64
}{
{"A", "a", 1},
{"B", "b", 300},
{"C", "c", 300},
}
for _, elem := range elems {
c.SetWithExpire(elem.key, elem.val, elem.ttl)
}
time.Sleep(1001 * time.Millisecond)
fmt.Printf("del info:%v\n", delCount)
fmt.Printf("expire info:%v\n", expireCount)
}
TODO
- Add Metrics statistics
- increase the control of memory usage
- Introduce an upgraded version of LRU: W-tinyLRU, which controls key writing and elimination more efficiently.