Created
April 6, 2020 06:17
-
-
Save calebschoepp/0165d92de412e288aa7441e792d0aa3a to your computer and use it in GitHub Desktop.
Code to wrap Bigcache with a generic interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package cache | |
import ( | |
"bytes" | |
"encoding/gob" | |
"errors" | |
"github.com/allegro/bigcache/v2" | |
) | |
type bigCache struct { | |
cache *bigcache.BigCache | |
} | |
// newBigCache returns a new BigCache struct | |
func newBigCache(cacheConfig *cacheConfig) (*bigCache, error) { | |
cache, err := bigcache.NewBigCache(bigcache.Config{ | |
Shards: 16, | |
LifeWindow: cacheConfig.ttl, | |
CleanWindow: cacheConfig.cleanFreq, | |
MaxEntriesInWindow: 1000 * 10 * 60, | |
MaxEntrySize: 500, | |
Verbose: false, | |
HardMaxCacheSize: cacheConfig.size, | |
StatsEnabled: true, | |
}) | |
if err != nil { | |
return nil, err | |
} | |
return &bigCache{ | |
cache: cache, | |
}, nil | |
} | |
// Set inserts the key/value pair into the cache. | |
// Only the exported fields of the given struct will be | |
// serialized and stored | |
func (c *bigCache) Set(key, value interface{}) error { | |
keyString, ok := key.(string) | |
if !ok { | |
return errors.New("a cache key must be a string") | |
} | |
valueBytes, err := serializeGOB(value) | |
if err != nil { | |
return err | |
} | |
return c.cache.Set(keyString, valueBytes) | |
} | |
// Get returns the value correlating to the key in the cache | |
func (c *bigCache) Get(key interface{}) (interface{}, error) { | |
// Assert the key is of string type | |
keyString, ok := key.(string) | |
if !ok { | |
return nil, errors.New("a cache key must be a string") | |
} | |
// Get the value in the byte format it is stored in | |
valueBytes, err := c.cache.Get(keyString) | |
if err != nil { | |
return nil, err | |
} | |
// Deserialize the bytes of the value | |
value, err := deserializeGOB(valueBytes) | |
if err != nil { | |
return nil, err | |
} | |
return value, nil | |
} | |
func serializeGOB(value interface{}) ([]byte, error) { | |
buf := bytes.Buffer{} | |
enc := gob.NewEncoder(&buf) | |
gob.Register(value) | |
err := enc.Encode(&value) | |
if err != nil { | |
return nil, err | |
} | |
return buf.Bytes(), nil | |
} | |
func deserializeGOB(valueBytes []byte) (interface{}, error) { | |
var value interface{} | |
buf := bytes.NewBuffer(valueBytes) | |
dec := gob.NewDecoder(buf) | |
err := dec.Decode(&value) | |
if err != nil { | |
return nil, err | |
} | |
return value, nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package cache | |
import ( | |
"time" | |
) | |
type cacheConfig struct { | |
size int // Size in MB | |
ttl time.Duration | |
cleanFreq time.Duration | |
} | |
// Interface to wrap any caching implementation | |
type Cache interface { | |
Set(key, value interface{}) error // Only exported fields in struct will be stored | |
Get(key interface{}) (interface{}, error) | |
} | |
// New builds a new default cache. You may pass options to modify the default values | |
func New(opts ...Option) (Cache, error) { | |
cacheConfig := &cacheConfig{ | |
size: 1, | |
ttl: 60 * time.Second, | |
cleanFreq: 30 * time.Second, | |
} | |
for _, opt := range opts { | |
opt.apply(cacheConfig) | |
} | |
cache, err := newBigCache(cacheConfig) | |
if err != nil { | |
return nil, err | |
} | |
return cache, nil | |
} | |
type Option interface { | |
apply(cacheConfig *cacheConfig) | |
} | |
type optionFunc func(*cacheConfig) | |
func (opt optionFunc) apply(cacheConfig *cacheConfig) { | |
opt(cacheConfig) | |
} | |
// WithSizeInMB sets the size of the cache in MBs | |
// The minimum size of the cache is 1 MB | |
// If a size of 0 or less is passed the cache will have unlimited size | |
func WithSizeInMB(size int) Option { | |
return optionFunc(func(cacheConfig *cacheConfig) { | |
cacheConfig.size = size | |
}) | |
} | |
// WithTTL will cause the cache to expire any item that lives longer | |
// than the given ttl | |
func WithTTL(ttl time.Duration) Option { | |
return optionFunc(func(cacheConfig *cacheConfig) { | |
cacheConfig.ttl = ttl | |
}) | |
} | |
// WithCleanFrequency sets how often the cache will clean out expired items | |
// The lowest the frequency may be is 1 second | |
// If the time is 0 then no cleaning will happen and items will never be removed | |
func WithCleanFrequency(cleanFreq time.Duration) Option { | |
return optionFunc(func(cacheConfig *cacheConfig) { | |
cacheConfig.cleanFreq = cleanFreq | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment