☔️ A complete Go cache library that brings you multiple ways of managing your caches

Overview

TravisBuildStatus GoDoc GoReportCard codecov

Gocache

Guess what is Gocache? a Go cache library. This is an extendable cache library that brings you a lot of features for caching data.

Overview

Here is what it brings in detail:

  • Multiple cache stores: actually in memory, redis, or your own custom store
  • A chain cache: use multiple cache with a priority order (memory then fallback to a redis shared cache for instance)
  • A loadable cache: allow you to call a callback function to put your data back in cache
  • A metric cache to let you store metrics about your caches usage (hits, miss, set success, set error, ...)
  • A marshaler to automatically marshal/unmarshal your cache values as a struct
  • Define default values in stores and override them when setting data
  • Cache invalidation by expiration time and/or using tags

Built-in stores

Built-in metrics providers

Available cache features in detail

A simple cache

Here is a simple cache instanciation with Redis but you can also look at other available stores:

Memcache

memcacheStore := store.NewMemcache(
	memcache.New("10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11212"),
	&store.Options{
		Expiration: 10*time.Second,
	},
)

cacheManager := cache.New(memcacheStore)
err := cacheManager.Set("my-key", []byte("my-value"), &store.Options{
	Expiration: 15*time.Second, // Override default value of 10 seconds defined in the store
})
if err != nil {
    panic(err)
}

value := cacheManager.Get("my-key")

cacheManager.Delete("my-key")

cacheManager.Clear() // Clears the entire cache, in case you want to flush all cache

Memory (using Bigcache)

bigcacheClient, _ := bigcache.NewBigCache(bigcache.DefaultConfig(5 * time.Minute))
bigcacheStore := store.NewBigcache(bigcacheClient, nil) // No otions provided (as second argument)

cacheManager := cache.New(bigcacheStore)
err := cacheManager.Set("my-key", []byte("my-value"), nil)
if err != nil {
    panic(err)
}

value := cacheManager.Get("my-key")

Memory (using Ristretto)

ristrettoCache, err := ristretto.NewCache(&ristretto.Config{
	NumCounters: 1000,
	MaxCost: 100,
	BufferItems: 64,
})
if err != nil {
    panic(err)
}
ristrettoStore := store.NewRistretto(ristrettoCache, nil)

cacheManager := cache.New(ristrettoStore)
err := cacheManager.Set("my-key", "my-value", &store.Options{Cost: 2})
if err != nil {
    panic(err)
}

value := cacheManager.Get("my-key")

cacheManager.Delete("my-key")

Redis

redisStore := store.NewRedis(redis.NewClient(&redis.Options{
	Addr: "127.0.0.1:6379",
}), nil)

cacheManager := cache.New(redisStore)
err := cacheManager.Set("my-key", "my-value", &store.Options{Expiration: 15*time.Second})
if err != nil {
    panic(err)
}

value, err := cacheManager.Get("my-key")
switch err {
	case nil:
		fmt.Printf("Get the key '%s' from the redis cache. Result: %s", "my-key", value)
	case redis.Nil:
		fmt.Printf("Failed to find the key '%s' from the redis cache.", "my-key")
	default:
	    fmt.Printf("Failed to get the value from the redis cache with key '%s': %v", "my-key", err)
}

Freecache

freecacheStore := store.NewFreecache(freecache.NewCache(1000), &Options{
	Expiration: 10 * time.Second,
})

cacheManager := cache.New(freecacheStore)
err := cacheManager.Set("by-key", []byte("my-value"), opts)
if err != nil {
	panic(err)
}

value := cacheManager.Get("my-key")

A chained cache

Here, we will chain caches in the following order: first in memory with Ristretto store, then in Redis (as a fallback):

// Initialize Ristretto cache and Redis client
ristrettoCache, err := ristretto.NewCache(&ristretto.Config{NumCounters: 1000, MaxCost: 100, BufferItems: 64})
if err != nil {
    panic(err)
}

redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})

// Initialize stores
ristrettoStore := store.NewRistretto(ristrettoCache, nil)
redisStore := store.NewRedis(redisClient, &store.Options{Expiration: 5*time.Second})

// Initialize chained cache
cacheManager := cache.NewChain(
    cache.New(ristrettoStore),
    cache.New(redisStore),
)

// ... Then, do what you want with your cache

Chain cache also put data back in previous caches when it's found so in this case, if ristretto doesn't have the data in its cache but redis have, data will also get setted back into ristretto (memory) cache.

A loadable cache

This cache will provide a load function that acts as a callable function and will set your data back in your cache in case they are not available:

// Initialize Redis client and store
redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
redisStore := store.NewRedis(redisClient, nil)

// Initialize a load function that loads your data from a custom source
loadFunction := func(key interface{}) (interface{}, error) {
    // ... retrieve value from available source
    return &Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}, nil
}

// Initialize loadable cache
cacheManager := cache.NewLoadable(
	loadFunction,
	cache.New(redisStore),
)

// ... Then, you can get your data and your function will automatically put them in cache(s)

Of course, you can also pass a Chain cache into the Loadable one so if your data is not available in all caches, it will bring it back in all caches.

A metric cache to retrieve cache statistics

This cache will record metrics depending on the metric provider you pass to it. Here we give a Prometheus provider:

// Initialize Redis client and store
redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
redisStore := store.NewRedis(redisClient, nil)

// Initializes Prometheus metrics service
promMetrics := metrics.NewPrometheus("my-test-app")

// Initialize metric cache
cacheManager := cache.NewMetric(
	promMetrics,
	cache.New(redisStore),
)

// ... Then, you can get your data and metrics will be observed by Prometheus

A marshaler wrapper

Some caches like Redis stores and returns the value as a string so you have to marshal/unmarshal your structs if you want to cache an object. That's why we bring a marshaler service that wraps your cache and make the work for you:

// Initialize Redis client and store
redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
redisStore := store.NewRedis(redisClient, nil)

// Initialize chained cache
cacheManager := cache.NewMetric(
	promMetrics,
	cache.New(redisStore),
)

// Initializes marshaler
marshal := marshaler.New(cacheManager)

key := BookQuery{Slug: "my-test-amazing-book"}
value := Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}

err = marshal.Set(key, value)
if err != nil {
    panic(err)
}

returnedValue, err := marshal.Get(key, new(Book))
if err != nil {
    panic(err)
}

// Then, do what you want with the  value

marshal.Delete("my-key")

The only thing you have to do is to specify the struct in which you want your value to be un-marshalled as a second argument when calling the .Get() method.

Cache invalidation using tags

You can attach some tags to items you create so you can easily invalidate some of them later.

Tags are stored using the same storage you choose for your cache.

Here is an example on how to use it:

// Initialize Redis client and store
redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
redisStore := store.NewRedis(redisClient, nil)

// Initialize chained cache
cacheManager := cache.NewMetric(
	promMetrics,
	cache.New(redisStore),
)

// Initializes marshaler
marshal := marshaler.New(cacheManager)

key := BookQuery{Slug: "my-test-amazing-book"}
value := Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}

// Set an item in the cache and attach it a "book" tag
err = marshal.Set(key, value, store.Options{Tags: []string{"book"}})
if err != nil {
    panic(err)
}

// Remove all items that have the "book" tag
err := marshal.Invalidate(store.InvalidateOptions{Tags: []string{"book"}})
if err != nil {
    panic(err)
}

returnedValue, err := marshal.Get(key, new(Book))
if err != nil {
	// Should be triggered because item has been deleted so it cannot be found.
    panic(err)
}

Mix this with expiration times on your caches to have a fine tuned control on how your data are cached.

Write your own custom cache

Cache respect the following interface so you can write your own (proprietary?) cache logic if needed by implementing the following interface:

type CacheInterface interface {
	Get(key interface{}) (interface{}, error)
	Set(key, object interface{}, options *store.Options) error
	Delete(key interface{}) error
	Invalidate(options store.InvalidateOptions) error
	Clear() error
	GetType() string
}

Or, in case you use a setter cache, also implement the GetCodec() method:

type SetterCacheInterface interface {
	CacheInterface

	GetCodec() codec.CodecInterface
}

As all caches available in this library implement CacheInterface, you will be able to mix your own caches with your own.

Write your own custom store

You also have the ability to write your own custom store by implementing the following interface:

type StoreInterface interface {
	Get(key interface{}) (interface{}, error)
	Set(key interface{}, value interface{}, options *Options) error
	Delete(key interface{}) error
	Invalidate(options InvalidateOptions) error
	Clear() error
	GetType() string
}

Of course, I suggest you to have a look at current caches or stores to implement your own.

Benchmarks

Benchmarks

Community

Please feel free to contribute on this library and do not hesitate to open an issue if you want to discuss about a feature.

Run tests

Generate mocks:

$ go get github.com/golang/mock/mockgen
$ make mocks

Test suite can be run with:

$ go test -v ./...
Comments
  • v4 issues - can't import

    v4 issues - can't import

    I'm trying to import the v4 but I can't, this error show up:

    go: finding module for package github.com/eko/gocache/v4/lib
    reallifeglobal.com/common/cache imports
    	github.com/eko/gocache/v4/lib: module github.com/eko/gocache@latest found (v1.2.0), but does not contain package github.com/eko/gocache/v4/lib
    

    To reproduce:

    • create a new project
    • try to follow installation steps
    opened by rwrz 6
  • Error while building go application, issue with github.com/XiaoMi/pegasus-go-client

    Error while building go application, issue with github.com/XiaoMi/pegasus-go-client

    Unable to build go application on go version 1.18.2 on MacOS and Ubuntu My guess is that problem is in this library: https://github.com/XiaoMi/pegasus-go-client which is used in github.com/eko/gocache/v3/store

    image

    Steps for Reproduction

    1. Use store.WithExpiration(5*time.Minute) in code Error is there even if using any other time value

    Expected behavior: App should build

    Actual behavior: Error while building application

    Error Log:

    divkix@Divs: …/AlitaGoRobot [ Beta][+3595 -17369][📦🗑️×22📝×41🛤️ ×2][🐳 desktop-linux][🐹 v1.18.2][🐏 11GiB/16GiB]
    ❯ go build .
    # github.com/XiaoMi/pegasus-go-client/idl/cmd
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:41:15: not enough arguments in call to iprot.ReadStructBegin
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:46:35: not enough arguments in call to iprot.ReadFieldBegin
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:60:26: not enough arguments in call to iprot.Skip
            have (thrift.TType)
            want (context.Context, thrift.TType)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:70:26: not enough arguments in call to iprot.Skip
            have (thrift.TType)
            want (context.Context, thrift.TType)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:75:25: not enough arguments in call to iprot.Skip
            have (thrift.TType)
            want (context.Context, thrift.TType)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:79:13: not enough arguments in call to iprot.ReadFieldEnd
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:83:12: not enough arguments in call to iprot.ReadStructEnd
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:90:15: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:99:18: not enough arguments in call to iprot.ReadListBegin
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:107:16: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/cmd/cmd.go:107:16: too many errors
    # github.com/XiaoMi/pegasus-go-client/idl/base
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/blob.go:18:15: not enough arguments in call to iprot.ReadBinary
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/blob.go:27:27: not enough arguments in call to oprot.WriteBinary
            have ([]byte)
            want (context.Context, []byte)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/error_code.go:94:18: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/error_code.go:99:27: not enough arguments in call to oprot.WriteString
            have (string)
            want (context.Context, string)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/gpid.go:18:12: not enough arguments in call to iprot.ReadI64
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/gpid.go:30:24: not enough arguments in call to oprot.WriteI64
            have (int64)
            want (context.Context, int64)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/rpc_address.go:26:18: not enough arguments in call to iprot.ReadI64
            have ()
            want (context.Context)
    vendor/github.com/XiaoMi/pegasus-go-client/idl/base/rpc_address.go:35:24: not enough arguments in call to oprot.WriteI64
            have (int64)
            want (context.Context, int64)
    

    Platforms:

    • MacOS Monterey Version 12.3.1 (21E258)
    • Ubuntu 20.04 LTS

    Include browser, operating system and respective versions

    Versions:

    • v3.0.0 (latest)

    Which versions are you running? Go Version: v1.18.2 gocache version: v3.0.0 OS: MacOS 12.3.1 and Ubuntu 20.04 LTS

    opened by Divkix 6
  • Unable to install v2 using go get

    Unable to install v2 using go get

    I tried go get -u github.com/eko/gocache/v2/cache And got

    # github.com/XiaoMi/pegasus-go-client/idl/base
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\blob.go:18:31: not enough arguments in call to iprot.ReadBinary
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\blob.go:27:26: not enough arguments in call to oprot.WriteBinary
            have ([]byte)
            want (context.Context, []byte)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\error_code.go:105:34: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\error_code.go:110:26: not enough arguments in call to oprot.WriteString
            have (string)
            want (context.Context, string)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\gpid.go:18:25: not enough arguments in call to iprot.ReadI64
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\gpid.go:30:23: not enough arguments in call to oprot.WriteI64
            have (int64)
            want (context.Context, int64)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\rpc_address.go:26:31: not enough arguments in call to iprot.ReadI64
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\base\rpc_address.go:35:23: not enough arguments in call to oprot.WriteI64
            have (int64)
            want (context.Context, int64)
    # github.com/XiaoMi/pegasus-go-client/idl/cmd
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:42:36: not enough arguments in call to iprot.ReadStructBegin
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:47:55: not enough arguments in call to iprot.ReadFieldBegin
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:61:25: not enough arguments in call to iprot.Skip
            have (thrift.TType)
            want (context.Context, thrift.TType)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:80:31: not enough arguments in call to iprot.ReadFieldEnd
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:84:31: not enough arguments in call to iprot.ReadStructEnd
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:91:31: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:100:37: not enough arguments in call to iprot.ReadListBegin
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:108:32: not enough arguments in call to iprot.ReadString
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:115:29: not enough arguments in call to iprot.ReadListEnd
            have ()
            want (context.Context)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:122:34: not enough arguments in call to oprot.WriteStructBegin
            have (string)
            want (context.Context, string)
    ..\..\go\pkg\mod\github.com\!xiao!mi\[email protected]\idl\cmd\cmd.go:122:34: too many errors
    
    

    Version 1.2 also does not work, with the same problem. OS: Windows 11 64 bit Golang: Version 1.17 Any help is appreciated. Thank you.

    opened by ffleader1 5
  • Wrong type for method Del when initialize Redis Store

    Wrong type for method Del when initialize Redis Store

    I want to implement a chained cache with Redis and BigCache. I don't have problem to declare Bigcache store, but have problem with redis. I using redis from "github.com/go-redis/redis", gocache store and gocache cache

    this is how i declare my cache:

    var LocationCache *cache.ChainCache
    
    func createCache(host string) *cache.ChainCache {
    	redisClient := redis.NewClient(&redis.Options{Addr: host})
    	bigcacheClient, _ := bigcache.NewBigCache(bigcache.DefaultConfig(720 * time.Hour))
    	bigcacheStorage := store.NewBigcache(bigcacheClient, nil)
    	redisStorage := store.NewRedis(redisClient, nil)
    
    	LocationCache = cache.NewChain(
    		cache.New(bigcacheStorage),
    		cache.New(redisStorage))
    	return LocationCache
    }
    

    But it give me this error: cannot use redisClient (variable of type *redis.Client) as store.RedisClientInterface value in argument to store.NewRedis: wrong type for method Del"

    Is anyone can help me? Thanks

    opened by Yosafat1997 5
  • Cannot implement StoreInterface's Invalidate method

    Cannot implement StoreInterface's Invalidate method

    Cannot figure out how we're supposed to implement Invalidate since the invalidation options are private. Cannot retrieve the tags that would be passed to the function.

    opened by sgtsquiggs 4
  • go mod unable to get latest version v2.2.0

    go mod unable to get latest version v2.2.0

    go mod tidy

    go: finding module for package github.com/eko/gocache github.com/eko/gocache: module github.com/eko/gocache@latest found (v1.2.0), but does not contain package github.com/eko/gocache

    import (
    	"github.com/eko/gocache"
    )
    
    func main() {
       //some code
    }
    
    opened by herozzm 4
  • Use hset for invalidate tag list instead of a string

    Use hset for invalidate tag list instead of a string

    The redis store uses a csv string in redis to keep the cache keys for each tag. This redis entry that keep all keys in csv format can grow fast if we group a lot of entries under the same tag. When accessing it, this big string is loaded in memory. This result to a big memory allocation/de-allocation every time an entry with a tag is written into the cache. To fix this issue, we need to change the format of the tag key. It would be better to use a hset. This way, we can read/write only the entry we need.

    opened by plbouchard 4
  • Implement GetWithTTL for Freecache

    Implement GetWithTTL for Freecache

    Freecache doesn't implement interface function GetWithTTL. I assume that the person that added Freecache integration didn't pull the latest changes from master.

    opened by corinapurcarea 4
  • Fix data races by removing unguarded goroutines; run mod tidy

    Fix data races by removing unguarded goroutines; run mod tidy

    Fixes #15

    • Remove goroutines that are causing data races
    • Also ran go mod tidy to remove unneeded modules

    Before

    $ go test -v -race ./...
    
    ...
    ==================
    WARNING: DATA RACE
    Read at 0x00c00014a9c8 by goroutine 58:
      reflect.typedmemmove()
          /usr/local/Cellar/go/1.13.4/libexec/src/runtime/mbarrier.go:177 +0x0
      reflect.packEface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:119 +0x103
      reflect.valueInterface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1033 +0x16f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1003 +0x38f7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:880 +0x25da
      fmt.(*pp).printArg()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:716 +0x2ee
      fmt.(*pp).doPrintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:1126 +0x912
      fmt.Sprintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:219 +0x73
      github.com/stretchr/testify/mock.Arguments.Diff()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:687 +0xf1a
      github.com/stretchr/testify/mock.(*Mock).findExpectedCall()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:269 +0x16a
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:348 +0xb3
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/metrics.(*MetricsInterface).RecordFromCodec()
          /Users/markphelps/workspace/gocache/test/mocks/metrics/metrics_interface.go:21 +0xaa
    
    Previous write at 0x00c00014a9c8 by goroutine 57:
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:385 +0x7b6
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/codec.(*CodecInterface).GetStore()
          /Users/markphelps/workspace/gocache/test/mocks/codec/codec_interface.go:83 +0x63
      github.com/eko/gocache/cache.(*ChainCache).setUntil()
          /Users/markphelps/workspace/gocache/cache/chain.go:90 +0x13b
    
    Goroutine 58 (running) created at:
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:65 +0x1bd
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:61 +0xff
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:31 +0xcb
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    
    Goroutine 57 (finished) created at:
      github.com/eko/gocache/cache.(*ChainCache).Get()
          /Users/markphelps/workspace/gocache/cache/chain.go:37 +0x383
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:29 +0x75
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c00014a9f8 by goroutine 58:
      reflect.typedmemmove()
          /usr/local/Cellar/go/1.13.4/libexec/src/runtime/mbarrier.go:177 +0x0
    --- FAIL: TestMetricSet (0.00s)
      reflect.packEface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:119 +0x103
        testing.go:853: race detected during execution of test
      reflect.valueInterface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1033 +0x16f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1003 +0x38f7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:880 +0x25da
      fmt.(*pp).printArg()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:716 +0x2ee
      fmt.(*pp).doPrintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:1126 +0x912
      fmt.Sprintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:219 +0x73
      github.com/stretchr/testify/mock.Arguments.Diff()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:687 +0xf1a
      github.com/stretchr/testify/mock.(*Mock).findExpectedCall()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:269 +0x16a
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:348 +0xb3
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/metrics.(*MetricsInterface).RecordFromCodec()
          /Users/markphelps/workspace/gocache/test/mocks/metrics/metrics_interface.go:21 +0xaa
    
    Previous write at 0x00c00014a9f8 by goroutine 57:
      sync/atomic.AddInt32()
          /usr/local/Cellar/go/1.13.4/libexec/src/runtime/race_amd64.s:269 +0xb
      sync.(*Mutex).Unlock()
          /usr/local/Cellar/go/1.13.4/libexec/src/sync/mutex.go:186 +0x51
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:405 +0x921
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/codec.(*CodecInterface).GetStore()
          /Users/markphelps/workspace/gocache/test/mocks/codec/codec_interface.go:83 +0x63
      github.com/eko/gocache/cache.(*ChainCache).setUntil()
          /Users/markphelps/workspace/gocache/cache/chain.go:90 +0x13b
    
    Goroutine 58 (running) created at:
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:65 +0x1bd
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:61 +0xff
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:31 +0xcb
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    === RUN   TestMetricDelete
    
    Goroutine 57 (finished) created at:
      github.com/eko/gocache/cache.(*ChainCache).Get()
          /Users/markphelps/workspace/gocache/cache/chain.go:37 +0x383
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:29 +0x75
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c0001f6870 by goroutine 58:
      reflect.typedmemmove()
          /usr/local/Cellar/go/1.13.4/libexec/src/runtime/mbarrier.go:177 +0x0
      reflect.packEface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:119 +0x103
      reflect.valueInterface()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1033 +0x16f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1003 +0x38f7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:869 +0xec7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:810 +0x283f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:810 +0x283f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:880 +0x25da
      fmt.(*pp).printArg()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:716 +0x2ee
      fmt.(*pp).doPrintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:1126 +0x912
      fmt.Sprintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:219 +0x73
      github.com/stretchr/testify/mock.Arguments.Diff()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:687 +0xf1a
      github.com/stretchr/testify/mock.(*Mock).findExpectedCall()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:269 +0x16a
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:348 +0xb3
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/metrics.(*MetricsInterface).RecordFromCodec()
          /Users/markphelps/workspace/gocache/test/mocks/metrics/metrics_interface.go:21 +0xaa
    
    Previous write at 0x00c0001f6870 by goroutine 57:
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:385 +0x775
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/codec.(*CodecInterface).GetStore()
          /Users/markphelps/workspace/gocache/test/mocks/codec/codec_interface.go:83 +0x63
      github.com/eko/gocache/cache.(*ChainCache).setUntil()
          /Users/markphelps/workspace/gocache/cache/chain.go:90 +0x13b
    
    Goroutine 58 (running) created at:
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:65 +0x1bd
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:61 +0xff
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:31 +0xcb
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    
    Goroutine 57 (finished) created at:
      github.com/eko/gocache/cache.(*ChainCache).Get()
          /Users/markphelps/workspace/gocache/cache/chain.go:37 +0x383
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:29 +0x75
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    ==================
    --- FAIL: TestMetricDelete (0.00s)
        testing.go:853: race detected during execution of test
    === RUN   TestMetricDeleteWhenError
    --- PASS: TestMetricDeleteWhenError (0.00s)
    === RUN   TestMetricInvalidate
    ==================
    WARNING: DATA RACE
    Read at 0x00c0002dcba0 by goroutine 58:
      reflect.Value.String()
          /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1845 +0x5d
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:761 +0x3156
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:869 +0xec7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:810 +0x283f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:869 +0xec7
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:810 +0x283f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:810 +0x283f
      fmt.(*pp).printValue()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:880 +0x25da
      fmt.(*pp).printArg()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:716 +0x2ee
      fmt.(*pp).doPrintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:1126 +0x912
      fmt.Sprintf()
          /usr/local/Cellar/go/1.13.4/libexec/src/fmt/print.go:219 +0x73
      github.com/stretchr/testify/mock.Arguments.Diff()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:687 +0xf1a
      github.com/stretchr/testify/mock.(*Mock).findExpectedCall()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:269 +0x16a
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:348 +0xb3
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/metrics.(*MetricsInterface).RecordFromCodec()
          /Users/markphelps/workspace/gocache/test/mocks/metrics/metrics_interface.go:21 +0xaa
    
    Previous write at 0x00c0002dcba0 by goroutine 57:
      github.com/stretchr/testify/assert.CallerInfo()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/assert/assertions.go:146 +0x3c3
      github.com/stretchr/testify/mock.(*Mock).MethodCalled()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:385 +0x57e
      github.com/stretchr/testify/mock.(*Mock).Called()
          /Users/markphelps/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:338 +0x1e4
      github.com/eko/gocache/test/mocks/codec.(*CodecInterface).GetStore()
          /Users/markphelps/workspace/gocache/test/mocks/codec/codec_interface.go:83 +0x63
      github.com/eko/gocache/cache.(*ChainCache).setUntil()
          /Users/markphelps/workspace/gocache/cache/chain.go:90 +0x13b
    
    Goroutine 58 (running) created at:
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:65 +0x1bd
      github.com/eko/gocache/cache.(*MetricCache).updateMetrics()
          /Users/markphelps/workspace/gocache/cache/metric.go:61 +0xff
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:31 +0xcb
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    
    Goroutine 57 (finished) created at:
      github.com/eko/gocache/cache.(*ChainCache).Get()
          /Users/markphelps/workspace/gocache/cache/chain.go:37 +0x383
      github.com/eko/gocache/cache.(*MetricCache).Get()
          /Users/markphelps/workspace/gocache/cache/metric.go:29 +0x75
      github.com/eko/gocache/cache.TestMetricGetWhenChainCache()
          /Users/markphelps/workspace/gocache/cache/metric_test.go:83 +0x714
      testing.tRunner()
          /usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0x199
    ==================
    --- FAIL: TestMetricInvalidate (0.00s)
        testing.go:853: race detected during execution of test
    ...
    FAIL
    

    After

    $ go test -v -race ./...
    
    ...
    === RUN   TestRistrettoGetType
    --- PASS: TestRistrettoGetType (0.00s)
    PASS
    ok      github.com/eko/gocache/store    (cached)
    ?       github.com/eko/gocache/test/mocks/cache [no test files]
    ?       github.com/eko/gocache/test/mocks/codec [no test files]
    ?       github.com/eko/gocache/test/mocks/metrics       [no test files]
    ?       github.com/eko/gocache/test/mocks/store [no test files]
    ?       github.com/eko/gocache/test/mocks/store/clients [no test files]
    
    opened by markphelps 4
  • Should Rueidis store implementation return string?

    Should Rueidis store implementation return string?

    Hi @eko, @rwrz,

    Thank you for adding rueidis integration. It is my honor that rueidis can be integrated into this popular cache library.

    Just wondering why doesn't Rueidis store implementation return a simple string instead of a rueidis.RedisResult? Are there some considerations I missed? I thought It would be much friendly if it returns a string to users like what go-redis integration does.

    For example:

    func (s *RueidisStore) Get(ctx context.Context, key any) (any, error) {
    	cmd := s.client.B().Get().Key(key.(string)).Cache()
    	str, err := s.client.DoCache(ctx, cmd, s.options.ClientSideCacheExpiration).ToString()
    	if rueidis.IsRedisNil(err) {
    		err = lib_store.NotFoundWithCause(err)
    	}
    	return str, err
    }
    

    Also, I noticed that there are some room for improvement:

    1. Each tag operation in setTags can be manually pipelined:
    func (s *RueidisStore) setTags(ctx context.Context, key any, tags []string) {
    	ttl := 720 * time.Hour
    	for _, tag := range tags {
    		tagKey := fmt.Sprintf(RueidisTagPattern, tag)
    		s.client.DoMulti(ctx,
    			s.client.B().Sadd().Key(tagKey).Member(key.(string)).Build(),
    			s.client.B().Expire().Key(tagKey).Seconds(int64(ttl.Seconds())).Build(),
    		)
    	}
    }
    
    1. I am going to release a new CacheTTL() method this weekend. It can retrieve the remaining client-side TTL of a cached message. Then, the GetWithTTL can be just like this:
    func (s *RueidisStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
    	cmd := s.client.B().Get().Key(key.(string)).Cache()
    	res := s.client.DoCache(ctx, cmd).Cache(), s.options.ClientSideCacheExpiration)
    	ttl := res.CacheTTL()
    	str, err := res.ToString()
    	if rueidis.IsRedisNil(err) {
    		err = lib_store.NotFoundWithCause(err)
    	}
    	return str, time.Duration(ttl) * time.Second, err
    }
    

    What do you think? I am happy to open a PR for the above changes. 😊

    opened by rueian 3
  • Fix for applyOptionsWithDefault not keeping unmodified options

    Fix for applyOptionsWithDefault not keeping unmodified options

    I assume this was just an oversight. I was expecting that any specified options would override defaults, but unspecified options would still use the default options.

    IE:

    // Given
    defaultOptions := &Options{
    	expiration: 25 * time.Second,
    }
    
    // When
    options := applyOptionsWithDefault(defaultOptions, WithCost(7))
    
    // Then
    assert.Equal(t, int64(7), options.cost)
    assert.Equal(t, 25*time.Second, options.expiration)
    
    opened by sgtsquiggs 3
  • I think it's better for Loadalbe to support singleflight?

    I think it's better for Loadalbe to support singleflight?

    With golang.org/x/sync/singleflight, it provides a duplicate function call suppression mechanism, maybe it's better for gocache Loadable to support this feature builtin.

    opened by yflau 0
  • store.Options does not expose its props, making StoreInterface challenging to implement

    store.Options does not expose its props, making StoreInterface challenging to implement

    Steps for Reproduction

    The docs suggest that the dev can use a custom store by implementing the StoreInterface. I'm working on a custom cache to use as part of the library's chain cache but one sticking point is the Set. Here is its definition:

    Set(ctx context.Context, key any, value any, options ...Option) error
    

    Option is a function that takes and configures an Options object, both from the library package. here is how the library passes it to my cache's setter: github

    Within the library's cache implementations, the configured Options object is then read to discern, for example, the expiration date of the cache entry: example

    The issue I face is that Options (declared here) makes all its properties private, so when I implement my own Set(), I have no way to read it to figure out when the cached entry should expire.

    How is my custom StoreInterface implementation supposed to figure out the caching options used?

    Expected behavior:

    Custom struct implementing StoreInterface can be used in the chain cache

    Actual behavior:

    Unable to implement interface correctly because the Options object passed to my implementation does not expose the options in public properties

    Platforms:

    All

    Versions:

    v3.1.1

    opened by RoxKilly 2
  • support olric

    support olric

    We're using https://github.com/buraksezer/olric, and it would be a good addition to have here. Would you be open to contribution of a new store?

    cc @buraksezer

    opened by derekperkins 0
  • Cannot write reliable tests for cache invalidation

    Cannot write reliable tests for cache invalidation

    I am testing a cache that I've written on top of eko/gocache. I need to wait for an invalidation to occur before I can move on to my next step. Currently I am waiting for the invalidation via:

    	// wait for cache invalidation
    	assert.Eventually(t, func() bool {
    		_, err = s.localStore.Get(ctx, key)
    		return err != nil
    	}, 10*time.Second, 10*time.Millisecond, "timed out waiting for local store")
    

    this loops for 10 seconds, checking every 10 milliseconds to see if the value has been ejected.

    Usually this passes, but sometimes it does not pass. I cannot figure out why this sometimes fails.

    I am using Invalidate and not Delete, so it is invalidating via the tag.

    Local store in this case is Ristretto.

    opened by sgtsquiggs 0
  • bigcache GetWithTTL not supported

    bigcache GetWithTTL not supported

    The bigcache implementation of GetWithTTL is just a hard coded 0 duration but with no indication in API it will not work as intended.

    I suggest instead returning an error that lets the user know that the method is in fact not supported and it should not be relied upon.

    Current behavior:

    // GetWithTTL returns data stored from a given key and its corresponding TTL
    func (s *BigcacheStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
    	item, err := s.Get(ctx, key)
    	return item, 0, err
    }
    

    Proposed change:

    ErrNotImplemented = errors.New("Method not implemented for codec")
    
    // GetWithTTL returns data stored from a given key and its corresponding TTL
    func (s *BigcacheStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
    	return nil, 0, store.ErrNotImplemented
    }
    
    opened by melpomene 0
  • Get method doesn't work correctly for bigcache with string type.

    Get method doesn't work correctly for bigcache with string type.

    Description Hi,

    Thank you for making the awesome library first.

    The type casting code is wrong for bigcache with string type. Because bigcache always returns []byte type(refer here) even though we can store data as string type.

    I think it is better if type casting is failed, it should return not only zero value but also error.

    Steps for Reproduction

    1. Set up Bigcache with string type
    func fn(config Config) {
      bigcacheClient, _ := bigcache.NewBigCache(bigcache.DefaultConfig(config.ExpiredTime))
      bigcacheStore := store.NewBigcache(bigcacheClient)
    
      memoryCache = cache.New[string](bigcacheStore)
    }
    
    1. Set data
    val := "some string data"
    memoryCache.Set(ctx, key, val) // There is no error because it will be convert to byte array
    
    1. Get data
    val, _ := memoryCache.Get(ctx, key) // There is no error but data is blank string.
    

    Expected behavior: It should be return stored string data.

    Actual behavior: It returned blank string.

    Platforms: Ubuntu

    Versions: v3.1.1

    opened by ByungJun25 1
Releases(store/rueidis/v4.1.3)
  • store/rueidis/v4.1.3(Dec 11, 2022)

    What's Changed

    • Make rueidis store to be more user-friendly and be more performant by @rueian in https://github.com/eko/gocache/pull/188

    New Contributors

    • @rueian made their first contribution in https://github.com/eko/gocache/pull/188

    Full Changelog: https://github.com/eko/gocache/compare/lib/v4.1.2...store/rueidis/v4.1.3

    Source code(tar.gz)
    Source code(zip)
  • lib/v4.1.2(Dec 3, 2022)

    Major changes

    Each store cache are now being separated into its own Go module, under the store directory, whine the library lives under the lib directory.

    You now have to import the following Go module to use the library:

    import "github.com/eko/gocache/lib/v4"
    

    and one or multiple cache store:

    import (
        "github.com/eko/gocache/store/bigcache/v4"
        "github.com/eko/gocache/store/freecache/v4"
        "github.com/eko/gocache/store/go_cache/v4"
        "github.com/eko/gocache/store/memcache/v4"
        "github.com/eko/gocache/store/pegasus/v4"
        "github.com/eko/gocache/store/redis/v4"
        "github.com/eko/gocache/store/rediscluster/v4"
        "github.com/eko/gocache/store/ristretto/v4"
    )
    

    What's Changed

    • Expose option details by @sgtsquiggs in https://github.com/eko/gocache/pull/181
    • Move each store into its own go module by @eko in https://github.com/eko/gocache/pull/183

    Full Changelog: https://github.com/eko/gocache/compare/v3.1.2...v4.0.0

    Source code(tar.gz)
    Source code(zip)
  • v3.1.2(Nov 10, 2022)

    What's Changed

    • Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0 by @dependabot in https://github.com/eko/gocache/pull/163
    • Bump github.com/coocood/freecache from 1.2.1 to 1.2.2 by @dependabot in https://github.com/eko/gocache/pull/164
    • Fix for applyOptionsWithDefault not keeping unmodified options by @sgtsquiggs in https://github.com/eko/gocache/pull/169
    • Bump github.com/dgraph-io/ristretto from 0.1.0 to 0.1.1 by @dependabot in https://github.com/eko/gocache/pull/170
    • Bump github.com/stretchr/testify from 1.8.0 to 1.8.1 by @dependabot in https://github.com/eko/gocache/pull/173
    • Bump github.com/coocood/freecache from 1.2.2 to 1.2.3 by @dependabot in https://github.com/eko/gocache/pull/174
    • Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1 by @dependabot in https://github.com/eko/gocache/pull/177
    • Bump github.com/prometheus/client_golang from 1.13.1 to 1.14.0 by @dependabot in https://github.com/eko/gocache/pull/179
    • chore: fixed unit tests and upgraded dependencies by @eko in https://github.com/eko/gocache/pull/180

    New Contributors

    • @sgtsquiggs made their first contribution in https://github.com/eko/gocache/pull/169

    Full Changelog: https://github.com/eko/gocache/compare/v3.1.1...v3.1.2

    Source code(tar.gz)
    Source code(zip)
  • v3.1.1(Jul 13, 2022)

    What's Changed

    • Update README.md - fixing go-redis link by @ralbt in https://github.com/eko/gocache/pull/154
    • Bump github.com/stretchr/testify from 1.7.2 to 1.7.4 by @dependabot in https://github.com/eko/gocache/pull/156
    • change style use tools gofumpt golangci-lint by @fmyxyz in https://github.com/eko/gocache/pull/155
    • Bump github.com/stretchr/testify from 1.7.4 to 1.7.5 by @dependabot in https://github.com/eko/gocache/pull/157
    • Bump github.com/stretchr/testify from 1.7.5 to 1.8.0 by @dependabot in https://github.com/eko/gocache/pull/158
    • Make store options publicly available by @eko in https://github.com/eko/gocache/pull/160

    New Contributors

    • @ralbt made their first contribution in https://github.com/eko/gocache/pull/154

    Full Changelog: https://github.com/eko/gocache/compare/v3.1.0...v3.1.1

    Source code(tar.gz)
    Source code(zip)
  • v3.1.0(Jun 14, 2022)

    What's Changed

    • Bump github.com/stretchr/testify from 1.7.1 to 1.7.2 by @dependabot in https://github.com/eko/gocache/pull/149
    • chain set to run set on all cache stores by @Ashtonian in https://github.com/eko/gocache/pull/146
    • add standard not found error by @Ashtonian in https://github.com/eko/gocache/pull/142
    • Updated error messages to be in lowercase by @eko in https://github.com/eko/gocache/pull/152

    New Contributors

    • @Ashtonian made their first contribution in https://github.com/eko/gocache/pull/146

    Full Changelog: https://github.com/eko/gocache/compare/v3.0.1...v3.1.0

    Source code(tar.gz)
    Source code(zip)
  • v3.0.1(Jun 6, 2022)

    What's Changed

    • Fix Loadable cache NPE when setting loaded value by @braginini in https://github.com/eko/gocache/pull/148

    New Contributors

    • @braginini made their first contribution in https://github.com/eko/gocache/pull/148

    Full Changelog: https://github.com/eko/gocache/compare/v3.0.0...v3.0.1

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(May 26, 2022)

    What's Changed

    • Reworked options to be variadics by @eko https://github.com/eko/gocache/pull/143
    • Added generics support by @eko in https://github.com/eko/gocache/pull/139

    This version have breaking changes in the 2 following changes:

    Reworked options

    We've updated options to use variadics instead of having a hard options argument at the end and often have to give nil.

    Instead of this:

    err := cacheManager.Set(ctx, "my-key", "my-value", nil)
    

    you will now be able to remove the nil argument:

    err := cacheManager.Set(ctx, "my-key", "my-value")
    

    and if you have some options to pass:

    err := cacheManager.Set(
        ctx,
        "my-key",
        "my-value",
        store.WithExpiration(10 * time.Second),
        store.WithTags([]string{"my-amazing-tag"}),
    )
    

    Generics support

    We have updated code base to use generics: you could still give any, such as the following:

    cacheManager := cache.New[any](redisStore)
    

    or, if you know you will store only a certain object, directly pass it:

    cacheManager := cache.New[*Book](redisStore)
    

    Full Changelog: https://github.com/eko/gocache/compare/v2.3.1...v3.0.0

    Source code(tar.gz)
    Source code(zip)
  • v2.3.1(May 22, 2022)

    What's Changed

    • Updated readme with install instructions by @coolblknerd in https://github.com/eko/gocache/pull/132
    • Bump github.com/smartystreets/assertions from 1.2.1 to 1.13.0 by @dependabot in https://github.com/eko/gocache/pull/133
    • Bump github.com/prometheus/client_golang from 1.12.1 to 1.12.2 by @dependabot in https://github.com/eko/gocache/pull/137
    • Bump github.com/spf13/cast from 1.4.1 to 1.5.0 by @dependabot in https://github.com/eko/gocache/pull/136
    • Make setTags method concurrency-safe by @Magomogo in https://github.com/eko/gocache/pull/138

    New Contributors

    • @coolblknerd made their first contribution in https://github.com/eko/gocache/pull/132
    • @Magomogo made their first contribution in https://github.com/eko/gocache/pull/138

    Full Changelog: https://github.com/eko/gocache/compare/v2.3.0...v2.3.1

    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Apr 13, 2022)

    What's Changed

    • Bump github.com/prometheus/client_golang from 1.12.0 to 1.12.1 by @dependabot in https://github.com/eko/gocache/pull/125
    • Bump github.com/coocood/freecache from 1.2.0 to 1.2.1 by @dependabot in https://github.com/eko/gocache/pull/128
    • Bump github.com/go-redis/redis/v8 from 8.11.4 to 8.11.5 by @dependabot in https://github.com/eko/gocache/pull/130
    • Bump github.com/stretchr/testify from 1.7.0 to 1.7.1 by @dependabot in https://github.com/eko/gocache/pull/129

    Full Changelog: https://github.com/eko/gocache/compare/v2.2.0...v2.3.0

    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Jan 23, 2022)

    What's Changed

    • Bump github.com/go-redis/redis/v8 from 8.9.0 to 8.10.0 by @dependabot in https://github.com/eko/gocache/pull/94
    • Bump github.com/dgraph-io/ristretto from 0.0.3 to 0.1.0 by @dependabot in https://github.com/eko/gocache/pull/95
    • mutex on codec to prevent race conditions by @coolka in https://github.com/eko/gocache/pull/97
    • Bump github.com/prometheus/client_golang from 1.10.0 to 1.11.0 by @dependabot in https://github.com/eko/gocache/pull/96
    • Bump github.com/golang/mock from 1.5.0 to 1.6.0 by @dependabot in https://github.com/eko/gocache/pull/98
    • Fix Race Condition in Memcached Store, Close Loadable to Stop Setter Routine by @terev in https://github.com/eko/gocache/pull/100
    • Bump github.com/go-redis/redis/v8 from 8.10.0 to 8.11.0 by @dependabot in https://github.com/eko/gocache/pull/102
    • Add patrickmn/go-cache to README by @Stoakes in https://github.com/eko/gocache/pull/103
    • Bump github.com/go-redis/redis/v8 from 8.11.0 to 8.11.1 by @dependabot in https://github.com/eko/gocache/pull/107
    • Bump github.com/spf13/cast from 1.3.1 to 1.4.0 by @dependabot in https://github.com/eko/gocache/pull/106
    • Bump github.com/go-redis/redis/v8 from 8.11.1 to 8.11.2 by @dependabot in https://github.com/eko/gocache/pull/108
    • fix instanciation typo by @derekperkins in https://github.com/eko/gocache/pull/110
    • Bump github.com/go-redis/redis/v8 from 8.11.2 to 8.11.3 by @dependabot in https://github.com/eko/gocache/pull/109
    • Bump github.com/spf13/cast from 1.4.0 to 1.4.1 by @dependabot in https://github.com/eko/gocache/pull/111
    • Bump github.com/go-redis/redis/v8 from 8.11.3 to 8.11.4 by @dependabot in https://github.com/eko/gocache/pull/112
    • Bumped to go 1.17 by @eko in https://github.com/eko/gocache/pull/113
    • Bump github.com/smartystreets/goconvey from 1.6.4 to 1.6.6 by @dependabot in https://github.com/eko/gocache/pull/115
    • Update LICENSE by @yashmit178 in https://github.com/eko/gocache/pull/116
    • Bump github.com/smartystreets/goconvey from 1.6.6 to 1.6.7 by @dependabot in https://github.com/eko/gocache/pull/117
    • Bump github.com/smartystreets/goconvey from 1.6.7 to 1.7.2 by @dependabot in https://github.com/eko/gocache/pull/119
    • Bump github.com/smartystreets/assertions from 1.2.0 to 1.2.1 by @dependabot in https://github.com/eko/gocache/pull/120
    • Bump github.com/coocood/freecache from 1.1.1 to 1.2.0 by @dependabot in https://github.com/eko/gocache/pull/121
    • Bump github.com/prometheus/client_golang from 1.11.0 to 1.12.0 by @dependabot in https://github.com/eko/gocache/pull/124
    • feat: Support for a key that would implement it's own key generator by @meDavidNS in https://github.com/eko/gocache/pull/123

    New Contributors

    • @coolka made their first contribution in https://github.com/eko/gocache/pull/97
    • @terev made their first contribution in https://github.com/eko/gocache/pull/100
    • @Stoakes made their first contribution in https://github.com/eko/gocache/pull/103
    • @derekperkins made their first contribution in https://github.com/eko/gocache/pull/110
    • @yashmit178 made their first contribution in https://github.com/eko/gocache/pull/116
    • @meDavidNS made their first contribution in https://github.com/eko/gocache/pull/123

    Full Changelog: https://github.com/eko/gocache/compare/v2.1.0...v2.2.0

    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Jun 1, 2021)

    What's Changed

    • Bump major version by @okhowang in https://github.com/eko/gocache/pull/93
    • Added context to loadFunction by @okhowang in https://github.com/eko/gocache/pull/92

    New Contributors

    • @okhowang made their first contribution in https://github.com/eko/gocache/pull/93

    Full Changelog: https://github.com/eko/gocache/compare/v2.0.0...v2.1.0

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(May 28, 2021)

    ⚠️ This is a new major release because of API BC break: context was added to all cache methods.

    New Features

    a61fbe3 Added context to cache methods

    Fixes

    4699aca fix: fix cacheManager use error with redis and pegasus

    Other

    b2b1571 Bump github.com/go-redis/redis/v8 from 8.8.3 to 8.9.0 4b757ae Bump github.com/go-redis/redis/v8 from 8.8.2 to 8.8.3 ef7808a Update README.md

    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(May 2, 2021)

    New Features

    6f0b55f Moved from travis-ci to github actions f5dcbee feat: add apache pegasus store

    Fixes

    f2a0586 fix: rebase latest and resolve comment 6ab2bc0 fixed rediscluster_test add context.TODO() aa3f31c fixed rediscluster_test ffd7fde fix: return an error instead panic as a fallback in bigcache store

    Other

    610ce4b Upgrade to GitHub-native Dependabot 6f7b7b8 build(deps): bump github.com/go-redis/redis/v8 from 8.8.0 to 8.8.2 5efb409 docs(readme): add descriptions about how to deal with the returned error from the redis storage ddc81b3 build(deps): bump github.com/go-redis/redis/v8 from 8.7.1 to 8.8.0 afb1aba mr 39e232c 支持 github.com/patrickmn/go-cache 51d1e1a build(deps): bump github.com/prometheus/client_golang 6ff1f44 update redis client to version 8 add redis cluster client tests 26e7da1 build(deps): bump github.com/golang/mock from 1.4.4 to 1.5.0 f23aa7d update bigcache version

    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Jan 20, 2021)

    New Features

    371ff1e Using simple set redis commands to invalidate tags (#3) 203d229 Replaced the tags csv string by a hset

    Other

    601b20d Ran go mod tidy e82ef1c build(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0 30ca7e3 build(deps): bump github.com/prometheus/client_golang 08eee2f Upgraded to go 1.15 and google.golang.org/appengine go v1.6.7

    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Nov 2, 2020)

    New Features

    a7ab8a7 Implement GetWithTTL for freecache 46a7306 implement interface function f31406a added new store (freecache) ab7dc64 propagate TTL 26e0f71 supporting ristretto ttl

    Fixes

    81bfa26 Removed strings.ToLower on cache key 8adc7b7 remove-log: drop logging in two places a3941db Do not hash key if its type is string 99d5369 fixing tests a9a0f81 doc: fix cache.Options => store.Options e3e61d7 doc: fix bigcache with wrong value type 24c4223 docs: fixed typo in README file 80c1102 Fix redis tags: redis client is returning strings

    Other

    cb3adf2 build(deps): bump github.com/prometheus/client_golang 9e5334e add test for invalid key 5579dbd more tests c36606f Added more tests 1db2250 go mod tidy b1c560c update documentation 0bc17c6 add tests for GetWithTTL 91984f7 tidy go sum 6fd2efd update documentation 0966a70 update tests 3810cba add gitignore 7f5df68 adding more test coverage 5663aac reverting proto support to give as separate PR fa5190f running go mod tidy c76d860 revert go.sum 6e3cd45 test coverage added 5f6b57c build(deps): bump github.com/golang/mock from 1.4.3 to 1.4.4 8fa11e5 build(deps): bump github.com/dgraph-io/ristretto from 0.0.2 to 0.0.3 7d3ab73 build(deps): bump github.com/prometheus/client_golang 497836c build(deps): bump github.com/prometheus/client_golang 0b50739 build(deps): bump github.com/stretchr/testify from 1.6.0 to 1.6.1 ef103e8 build(deps): bump github.com/go-redis/redis/v7 from 7.3.0 to 7.4.0 4d3fd48 build(deps): bump github.com/stretchr/testify from 1.5.1 to 1.6.0 8b9ffef build(deps): bump github.com/go-redis/redis/v7 from 7.2.0 to 7.3.0 70f9205 revert d9c5f5b test with go1.13-issue with using forks in other projects 04e814f build(deps): bump github.com/prometheus/client_golang 3a8bb97 build(deps): bump github.com/prometheus/client_golang b65b9d3 build(deps): bump github.com/golang/mock from 1.4.1 to 1.4.3 9923c46 build(deps): bump github.com/prometheus/client_golang 5ca9b93 Upgrade to go1.14 b85d8c5 build(deps): bump github.com/golang/mock from 1.4.0 to 1.4.1 79b65cf build(deps): bump github.com/go-redis/redis/v7 from 7.1.0 to 7.2.0 c798b07 build(deps): bump github.com/stretchr/testify from 1.4.0 to 1.5.1 3896b7a build(deps): bump github.com/go-redis/redis/v7 from 7.0.1 to 7.1.0 3ab560a build(deps): bump github.com/go-redis/redis/v7 1ce8623 build(deps): bump github.com/prometheus/client_golang a9fa870 build(deps): bump github.com/go-redis/redis/v7 7b615f6 build(deps): bump github.com/prometheus/client_golang 5ded7b2 build(deps): bump github.com/golang/mock from 1.3.1 to 1.4.0 f3f6db0 build(deps): bump github.com/go-redis/redis/v7 edd0dad build(deps): bump github.com/prometheus/client_golang

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Dec 8, 2019)

    New Features

    140d01b Using promauto to initialize the metric and testutil in tests 7847237 feat: added Clear() method to flush all cache data 16d5e61 docs: added some store benchmarks

    Fixes

    3b46630 fix(tests): replace mockery by gomock and fix race conditions 19c7bf8 test: rework unit tests to isolate mocks and add missing cases 0627751 style: fixed mispelling words and missing comments

    Other

    2c2e7d2 build(deps): bump github.com/coreos/etcd 871607c build(deps): bump github.com/prometheus/client_golang f9ee181 docs: updated README.md with custom cache/store interfaces 256357e doc: added codecov badge 9de424d ci: added codecov to .travis.yml

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Oct 19, 2019)

    New Features

    ff78752 feat: added cache invalidation by tags 238e4ef feat: added cache & store delete() method

    Other

    8de72e9 Added .github files

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Oct 15, 2019)

    New Features

    d28a9c2 feat: allow store options override in Set() method 4e751dc feat(store): added bigcache 23439ea feat(cache): added options 7f5503c feat(store): added memcache support

    Fixes

    bd0c5fb fix(README): typo in code example

    Other

    e78581a BREAKING CHANGE: renamed eko/gache to eko/gocache 41b785b docs: added README and travis.yml

    Source code(tar.gz)
    Source code(zip)
Owner
Vincent Composieux
Freelance web architect who loves code and infrastructure. Issue solver around various technologies.
Vincent Composieux
Fast in-memory key:value store/cache with TTL

MCache library go-mcache - this is a fast key:value storage. Its major advantage is that, being essentially a thread-safe . map[string]interface{} wit

O.J 86 Nov 11, 2022
Cache Slow Database Queries

Cache Slow Database Queries This package is used to cache the results of slow database queries in memory or Redis. It can be used to cache any form of

null 122 Dec 19, 2022
Golang LRU cache

golang-lru This provides the lru package which implements a fixed-size thread safe LRU cache. It is based on the cache in Groupcache. Documentation Fu

HashiCorp 3.3k Dec 29, 2022
A fast little LRU cache for Go

tinylru A fast little LRU cache. Getting Started Installing To start using tinylru, install Go and run go get: $ go get -u github.com/tidwall/tinylru

Josh Baker 128 Dec 24, 2022
A skip list of arbitrary elements that can be filtered using roaring bitmaps stored in an LRU cache

Skipfilter This package provides a data structure that combines a skiplist with a roaring bitmap cache. This library was created to efficiently filter

Kevin Burns 22 Aug 4, 2022
A threadsafe single-value cache for Go with a simple but flexible API

SVCache SVCache is a threadsafe, single-value cache with a simple but flexible API. When there is no fresh value in the cache, an attempt to retrieve

softwaretechnik.berlin 1 Jan 23, 2022
A tree like tool help you to explore data structures in your redis server

Redis-view is a tree like tool help you explore data structures in your redis server

dreamersdw 19 Mar 17, 2022
Graphoscope: a solution to access multiple independent data sources from a common UI and show data relations as a graph

Graphoscope A solution to access multiple independent data sources from a common UI and show data relations as a graph: Contains a list of by default

CERT.LV 29 May 26, 2022
Stalin sort in multiple languages!

stalin-sort Stalin sort in multiple languages, contributions are welcome! Motivation This repo is motivated by this tweet, this tweet contains a refer

CaKrome 0 Jan 14, 2022
The Go library that will drive you to AOP world!

Beyond The Golang library that will drive you to the AOP paradigm world! Check Beyond Documentation What's AOP? In computing, aspect-oriented programm

Wesovi Labs 52 Dec 6, 2022
Mapreduce - A in-process MapReduce tool to help you to optimize service response time.

mapreduce English | 简体中文 Why we have this repo? mapreduce is part of go-zero, but a few people asked if mapreduce can be used separately. But I recomm

Kevin Wan 126 Jan 4, 2023
Custom generic HTTP handler providing automatic JSON decoding/encoding of HTTP request/response to your concrete types

gap Custom generic HTTP handler providing automatic JSON decoding/encoding of HTTP request/response to your concrete types. gap.Wrap allows to use the

Ectobit 1 Aug 28, 2022
GoStruct2Table - format your struct like a table.

GoStruct2Table format your struct like a table. Installing $ go get -u -v github.com/runningzyp/GoStruct2Table Simple Example import parser "github.c

YunpengZhan 10 Aug 15, 2022
A small flexible merge library in go

conjungo A merge utility designed for flexibility and customizability. The library has a single simple point of entry that works out of the box for mo

InVision 109 Dec 27, 2022
Golang string comparison and edit distance algorithms library, featuring : Levenshtein, LCS, Hamming, Damerau levenshtein (OSA and Adjacent transpositions algorithms), Jaro-Winkler, Cosine, etc...

Go-edlib : Edit distance and string comparison library Golang string comparison and edit distance algorithms library featuring : Levenshtein, LCS, Ham

Hugo Bollon 373 Dec 20, 2022
Go native library for fast point tracking and K-Nearest queries

Geo Index Geo Index library Overview Splits the earth surface in a grid. At each cell we can store data, such as list of points, count of points, etc.

Hailo Network IP Ltd 344 Dec 3, 2022
Data structure and algorithm library for go, designed to provide functions similar to C++ STL

GoSTL English | 简体中文 Introduction GoSTL is a data structure and algorithm library for go, designed to provide functions similar to C++ STL, but more p

stirlingx 752 Dec 26, 2022
Zero allocation Nullable structures in one library with handy conversion functions, marshallers and unmarshallers

nan - No Allocations Nevermore Package nan - Zero allocation Nullable structures in one library with handy conversion functions, marshallers and unmar

Andrey Kuzmin 63 Dec 20, 2022
A Go library for an efficient implementation of a skip list: https://godoc.org/github.com/MauriceGit/skiplist

Fast Skiplist Implementation This Go-library implements a very fast and efficient Skiplist that can be used as direct substitute for a balanced tree o

Maurice Tollmien 240 Dec 30, 2022