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

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 := cacheManager.Get("my-key")

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 ./...
Owner
Vincent Composieux
Freelance web architect who loves code and infrastructure. Issue solver around various technologies.
Vincent Composieux
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
  • 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

  • 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.

  • 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

  • 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.

  • 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
    }
    
  • 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.

  • 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.

  • 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]
    
  • 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. 😊

  • 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)
    
  • 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.

  • 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

  • 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

  • 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.

  • 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
    }
    
  • 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

Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.
Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.

GCache Cache library for golang. It supports expirable Cache, LFU, LRU and ARC. Features Supports expirable Cache, LFU, LRU and ARC. Goroutine safe. S

Dec 30, 2022
An in-memory cache library for golang. It supports multiple eviction policies: LRU, LFU, ARC

GCache Cache library for golang. It supports expirable Cache, LFU, LRU and ARC. Features Supports expirable Cache, LFU, LRU and ARC. Goroutine safe. S

May 31, 2021
Dec 28, 2022
Package cache is a middleware that provides the cache management for Flamego.

cache Package cache is a middleware that provides the cache management for Flamego. Installation The minimum requirement of Go is 1.16. go get github.

Nov 9, 2022
A mem cache base on other populator cache, add following feacture

memcache a mem cache base on other populator cache, add following feacture add lazy load(using expired data, and load it asynchronous) add singlefligh

Oct 28, 2021
Cache - A simple cache implementation

Cache A simple cache implementation LRU Cache An in memory cache implementation

Jan 25, 2022
Gin-cache - Gin cache middleware with golang

Gin-cache - Gin cache middleware with golang

Nov 28, 2022
groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.

groupcache Summary groupcache is a distributed caching and cache-filling library, intended as a replacement for a pool of memcached nodes in many case

Dec 31, 2022
An in-memory key:value store/cache (similar to Memcached) library for Go, suitable for single-machine applications.

go-cache go-cache is an in-memory key:value store/cache similar to memcached that is suitable for applications running on a single machine. Its major

Dec 29, 2022
gdcache is a pure non-intrusive distributed cache library implemented by golang
gdcache is a pure non-intrusive distributed cache library implemented by golang

gdcache is a pure non-intrusive distributed cache library implemented by golang, you can use it to implement your own distributed cache

Sep 26, 2022
A REST-API service that works as an in memory key-value store with go-minimal-cache library.

A REST-API service that works as an in memory key-value store with go-minimal-cache library.

Aug 25, 2022
A multi-level cache library with stampede prevention for Go

HybridCache A multi-level cache library with cache stampede prevention for Go import "github.com/cshum/hybridcache" // Redis cache adapter based on R

Nov 21, 2022
An in-memory key:value store/cache library written in Go 1.18 generics

go-generics-cache go-generics-cache is an in-memory key:value store/cache that is suitable for applications running on a single machine. This in-memor

Dec 27, 2022
Ristretto - A fast, concurrent cache library built with a focus on performance and correctness

Ristretto Ristretto is a fast, concurrent cache library built with a focus on pe

Aug 21, 2022
Gocodecache - An in-memory cache library for code value master in Golang

gocodecache An in-memory cache library for code master in Golang. Installation g

Jun 23, 2022
A zero-dependency cache library for storing data in memory with generics.

Memory Cache A zero-dependency cache library for storing data in memory with generics. Requirements Golang 1.18+ Installation go get -u github.com/rod

May 26, 2022
LevelDB style LRU cache for Go, support non GC object.

Go语言QQ群: 102319854, 1055927514 凹语言(凹读音“Wa”)(The Wa Programming Language): https://github.com/wa-lang/wa LRU Cache Install go get github.com/chai2010/c

Jul 5, 2020
fastcache - fast thread-safe inmemory cache for big number of entries in Go

Fast thread-safe inmemory cache for big number of entries in Go. Minimizes GC overhead

Dec 27, 2022