☔️ 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, 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 ./...
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

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

Nov 11, 2022
Cache Slow Database Queries
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

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

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

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

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

Jan 23, 2022
A tree like tool help you to explore data structures in your redis server
 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

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

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

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

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

Dec 6, 2022
Mapreduce - A in-process MapReduce tool to help you to optimize service response time.
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

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

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

Aug 15, 2022
A small flexible merge library in go
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

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

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.

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

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

Dec 20, 2022
A Go library for an efficient implementation of a skip list: https://godoc.org/github.com/MauriceGit/skiplist
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

Dec 30, 2022