zcache is an in-memory key:value store/cache with time-based evictions.
It is suitable for applications running on a single machine. Its major advantage
is that it's essentially a thread-safe map with expiration times. Any object can
be stored, for a given duration or forever, and the cache can be safely used by
multiple goroutines.
Although zcache isn't meant to be used as a persistent datastore, the contents
can be saved to and loaded from a file (using c.Items() to retrieve the items
map to serialize, and NewFrom() to create a cache from a deserialized one) to
recover from downtime quickly.
The canonical import path is zgo.at/zcache, and reference docs are at
https://godocs.io/zgo.at/zcache
This is a fork of https://github.com/patrickmn/go-cache – which no longer seems
actively maintained. There are two versions of zcache:
- v1 is intended to be 100% compatible with co-cache and a drop-in replacement
with various enhancements.
- v2 makes various incompatible changes to the API: various functions calls are
improved. This uses generics and requires Go 1.18.
This README documents v2; see README.v1.md for the v1 README. Both versions are
maintained. See the "changes" section below for a list of changes.
Usage
Some examples from example_test.go:
func ExampleSimple() {
c := zcache.New[string, string](5*time.Minute, 10*time.Minute)
c.Set("foo", "bar")
c.SetWithExpire("baz", "never", zcache.NoExpiration)
foo, ok := c.Get("foo")
if ok {
fmt.Println(foo)
}
}
func ExampleStruct() {
type MyStruct struct{ Value string }
c := zcache.New[string, *MyStruct](zcache.NoExpiration, zcache.NoExpiration)
c.Set("cache", &MyStruct{Value: "value"})
v, _ := c.Get("cache")
fmt.Printf("%#v\n", v)
}
func ExampleAny() {
c := zcache.New[string, any](zcache.NoExpiration, zcache.NoExpiration)
c.Set("a", "value 1")
c.Set("b", 42)
a, _ := c.Get("a")
b, _ := c.Get("b")
p := func(a string, b int) { fmt.Println(a, b) }
p(a.(string), b.(int))
}
func ExampleProxy() {
type Site struct {
ID int
Hostname string
}
site := &Site{
ID: 42,
Hostname: "example.com",
}
c := zcache.New[int, *Site](zcache.NoExpiration, zcache.NoExpiration)
p := zcache.NewProxy[string, int, *Site](c)
p.Set(42, "example.com", site)
siteByID, ok := c.Get(42)
fmt.Printf("%v %v\n", ok, siteByID)
siteByHost, ok := p.Get("example.com")
fmt.Printf("%v %v\n", ok, siteByHost)
fmt.Printf("%v\n", siteByID == siteByHost)
}
Changes
Incompatible changes in v2
-
Use type parameters instead of map[string]interface{}; you can get the same
as before with zcache.New[string, any](..), but if you know you will only
store MyStruct you can use zcache.New[string, *MyStruct](..) for
additional type safety.
-
Remove Save(), SaveFile(), Load(), LoadFile(); you can still persist
stuff to disk by using Items() and NewFrom(). These methods were already
deprecated.
-
Rename Set() to SetWithExpire(), and rename SetDefault() to Set().
Most of the time you want to use the default expiry time, so make that the
easier path.
-
The Increment* and Decrement* functions have been removed; you can replace
them with Modify():
cache := New[string, int](DefaultExpiration, 0)
cache.Set("one", 1)
cache.Modify("one", func(v int) int { return v + 1 })
The performance of this is roughly the same as the old Increment, and this is
a more generic method that can also be used for other things like appending to
a slice.
-
Rename Flush() to Reset(); I think that more clearly conveys what it's
intended for as Flush() is typically used to flush a buffer or the like.
Compatible changes from go-cache
All these changes are in both v1 and v2:
- Add
Keys() to list all keys.
- Add
Touch() to update the expiry on an item.
- Add
GetStale() to get items even after they've expired.
- Add
Pop() to get an item and delete it.
- Add
Modify() to atomically modify existing cache entries (e.g. lists, maps).
- Add
DeleteAll() to remove all items from the cache with onEvicted call.
- Add
DeleteFunc() to remove specific items from the cache atomically.
- Add
Rename() to rename keys, retaining the value and expiry.
- Add
Proxy type, to access cache items under a different key.
- Various small internal and documentation improvements.
See issue-list.markdown for a complete run-down of the
PRs/issues for go-cache and what was and wasn't included.
FAQ
How can I limit the size of the cache? Is there an option for this?
Not really; zcache is intended as a thread-safe map with time-based eviction.
This keeps it nice and simple. Adding something like a LRU eviction mechanism
not only makes the code more complex, it also makes the library worse for cases
where you just want a map since it requires additional memory and makes some
operations more expensive (unless a new API is added which make the API worse
for those use cases).
So unless I or someone else comes up with a way to do this which doesn't detract
anything from the simple map use case, I'd rather not add it. Perhaps wrapping
zcache.Cache and overriding some methods could work, but I haven't looked at
it.
tl;dr: this isn't designed to solve every caching use case. That's a feature.